HomeVideos

How To De-Slop A Codebase Ruined By AI (with one skill)

Now Playing

How To De-Slop A Codebase Ruined By AI (with one skill)

Transcript

353 segments

0:00

You've probably seen the thousands of

0:01

LinkedIn CEO posts saying that code is

0:04

cheap and they can move faster than ever

0:06

before. But what's happening is that AI

0:08

has simply accelerated software entropy.

0:11

In other words, codebases are falling

0:13

apart faster than they ever have before

0:16

because every time that you make a

0:17

change that doesn't take into account

0:18

the entire codebase, you are likely to

0:21

introduce little things, weird things

0:23

that make the codebase harder to change.

0:25

And over time, that just snowballs and

0:27

snowballs until you end up with a huge

0:29

ball of mud. Sloppy, sloppy mud that is

0:32

incredibly hard to reverse if you don't

0:35

know how to do it. I've made a video

0:36

about this before introducing folks to

0:38

the idea of deep modules. And that video

0:40

focuses more on prevention, how you can

0:42

prevent your setup from getting to that

0:45

point. Let's now focus on the cure, how

0:47

you can take a codebase that feels like

0:49

it's beyond repair and rescue it. And

0:51

you can do that with some good old

0:53

software fundamentals, as well as my

0:55

improved codebase architecture skill.

0:57

We're going to be walking through what

0:58

this skill does, revisiting some of the

1:00

terms we looked at in the other video,

1:02

then we're going to take that and apply

1:03

it to a real codebase. And this, by the

1:05

way, is part of my GitHub Skills Repo,

1:08

which is currently sitting at 41.5k

1:10

stars. Bonkers. Now, one of the things

1:12

that I added to this improved codebase

1:14

architecture skill recently was a

1:16

glossary of terminology. Having a shared

1:18

vocabulary with the AI is super

1:20

important because it means that you can

1:22

talk using the same language. You can

1:24

understand what each other's language is

1:27

and you can be a lot more precise with

1:29

what you're asking for. This terminology

1:31

here is super duper useful and I'm going

1:34

to spend a portion of this video going

1:35

through what each of these terms

1:36

actually mean. Honestly, just

1:38

understanding this stuff at a deep level

1:39

will make you a better software

1:41

developer. So, let's get started by

1:42

talking about modules. A module is a

1:45

unit of something in your application.

1:48

It could be a bunch of React components

1:50

that all fit together to form a page. It

1:52

could be a bunch of functions inside

1:54

your application that are entirely

1:55

responsible for authentication. Or it

1:57

could simply be the logger that you've

1:58

chosen, like a log into the console, log

2:01

into a file, or log into a third-party

2:02

service. In a good code base, these

2:04

modules talk to each other, and they

2:06

talk to each other via their interfaces.

2:08

An interface is everything a caller must

2:10

know to use the module correctly. For

2:12

instance, if it's an authentication

2:13

module, then it might have a sign-in

2:15

method, it might have a sign-out method.

2:17

And these methods are the interface to

2:19

that module. The methods are not the

2:21

only thing that's important, the

2:22

interface also includes kind of nebulous

2:24

information about how to call the

2:26

module. So, perhaps it's documentation,

2:28

too. The implementation is then what's

2:30

inside the module, what it actually does

2:32

when you call sign-in or sign-out. And

2:34

so, this is the core primitive that

2:35

we're talking about, the modules that

2:37

have interfaces and implementations

2:39

scattered throughout your application.

2:40

These modules can either be deep

2:42

modules, or they can be shallow modules.

2:45

A deep module hides lots of

2:47

implementation behind a relatively

2:49

simple interface. A shallow module has a

2:51

complex interface and kind of not much

2:54

implementation actually behind it. These

2:56

ideas are from John Osterhout's book A

2:58

Philosophy of Software Design, which I

3:00

recommend you pick up a copy of. Deep

3:01

modules are considered better than

3:02

shallow modules because it hides more

3:04

information away from the caller. In

3:06

other words, the person who's calling

3:07

this, or the function that's calling

3:09

this, only needs to know about this tiny

3:10

little interface, and they'll get access

3:12

to all of this implementation. Lovely.

3:14

And so, that's what we describe as

3:15

depth, the amount of behavior a caller

3:17

can exercise per unit of interface that

3:20

they have to learn. Really good

3:21

open-source libraries, like TanStack

3:23

Query or something, have really good

3:26

deep modules. In other words, they're

3:27

hiding a lot of complexity behind a

3:29

super simple interface. These modules

3:31

then interact with each other, and they

3:33

have dependencies on each other. For

3:34

instance, this module might interact

3:36

with this module here, which then

3:38

interacts with this module up here, and

3:40

this module up here, and they have these

3:41

dependency

3:45

are called the seams. It's the location

3:47

at which the modules interface lives

3:49

inside the application. These seams are

3:51

usually where you're going to do your

3:53

unit testing or your integration

3:54

testing. For instance, if we wanted to

3:56

test this module in isolation down here,

3:59

then we would add a mock or something

4:01

just at this seam. So, figuring out

4:03

where your seams are going to live in

4:05

your application is crucial to getting a

4:06

good architecture. When you find out

4:08

where a seam is in your application, you

4:09

need some concrete thing, a module, that

4:12

satisfies that interface. This is what

4:14

I'm going to call an adapter, which I'm

4:16

taking from hexagonal architecture. For

4:18

instance, if you have some kind of

4:19

application that depends on a clock

4:21

running, then you may want to have a

4:23

clock, a normal clock inside here using

4:26

the actual living clock, and then inside

4:28

some tests, you may want to have an

4:29

adapter that is a fake clock. These both

4:32

satisfy the interface at that seam, and

4:34

it means that you can use the fake clock

4:36

in tests, so you don't have to literally

4:38

wait 2 weeks for your test to finish.

4:40

So, that's how seams and adapters play

4:42

together. The benefit of all this is

4:44

that these deep modules have two main

4:47

properties or two main benefits that you

4:48

get from them. For the maintainers, the

4:49

people maintaining this module, they get

4:51

locality. Changes to that module and

4:54

bugs and all the fixes to do with them,

4:56

they concentrate in one place, in that

4:58

deep module. If it's scattered around

5:00

over multiple different modules, then

5:02

you have low locality. You want high

5:04

locality, grouping and co-locating the

5:07

things that matter and that often change

5:09

together. The people using this module

5:11

will get more leverage the deeper the

5:13

module is. In other words, more

5:14

capability per unit of interface they

5:16

have to learn. And so, when we're

5:17

talking about improving our code bases,

5:20

these are the two attributes that we're

5:21

aiming at. Right, that's enough

5:22

knowledge, we know the basic terms of

5:24

engagement. Now, let's go and improve a

5:26

code base. The code base we're going to

5:27

look at is my course video manager code

5:30

base, which is the repo of software that

5:33

I'm actually using to record this video.

5:34

This code base has had, ooh, around

5:36

1,500 commits here, and I wouldn't say

5:39

it's a ball of mud, but I also wouldn't

5:41

say it's perfect, either. It's a React

5:43

Router application, it uses effect.ts

5:45

under the hood. Uh, let's get into it.

5:47

I'm going to open up a new Claude

5:48

session inside here and I'm going to run

5:50

my improved code base architecture skill

5:53

and we'll turn off auto mode. Auto mode

5:55

does some funny things with these human

5:56

in the loop style flows and so I don't

5:58

want it on here. We can see it's going

6:00

and exploring and looking through the

6:02

code. That's what it's instructed to do

6:04

first. Here we go. Explore architecture

6:06

for deepening opportunities. Usually a

6:08

bad code base is one that has a ton of

6:10

shallow modules in it or one that has

6:12

very poor leverage for those modules or

6:14

poor locality where lots of stuff is

6:16

spread in lots of different places.

6:18

Okay, it's come back with some

6:19

candidates here. Let's bump up the

6:21

screen size and hopefully Claude code

6:23

won't just destroy itself. Okay, I guess

6:25

maybe we're not bumping up the screen

6:27

size. Thank you for that Claude code. We

6:28

can see it's identified six deepening

6:30

opportunities here. These candidates

6:32

here are pretty hard to explain because

6:34

they sort of require domain knowledge

6:35

about my repo, but we can see here that

6:37

it's saying that there's a concept that

6:39

doesn't have a single seam. In other

6:41

words, there are two implementations of

6:43

this insertion point and they live

6:45

and the seam where they must be

6:47

untested. This essentially means that

6:49

the front end could make some changes

6:51

but the back end because it has a

6:53

separate parallel implementation could

6:55

be out of sync with it. So this I think

6:57

is actually a really good candidate for

6:59

refactoring into a single module. We

7:00

gain locality. And it says that here we

7:02

would gain locality interleaved clip

7:04

clip section ordering rule lives in one

7:06

place. So let's go and take a look at

7:09

that. Let's actually say, yeah, I'd like

7:11

to pick one here. That seems like a good

7:13

candidate. So let's fire that off and

7:15

see what it says. Okay, Claude is

7:16

trolling me here. It says, I'd like to

7:17

pick one. I meant I meant one. Great.

7:21

Okay, so it now has come back with it's

7:23

got concrete code on both sides to to

7:25

ground this and it enters a grilling

7:27

session. And in this grilling session we

7:29

can take the ideas inside here and we

7:32

can start kind of talking about what a

7:34

better solution would be. This is a nice

7:35

sentence here. The back end has no end.

7:38

Let's not think about that too

7:39

literally. What you end up doing with

7:41

this skill is you end up talking about

7:43

the potential proposed solution, and it

7:45

will then propose a shape. And once

7:47

that's all done, you can take that and

7:49

you can put that in as a GitHub issue

7:50

into your issue tracker, which can then

7:52

be picked up by an AFK agent. You should

7:54

check out my video on Sandcastle if

7:56

you're interested in that. Now, in the

7:57

course of normal development, what I

7:59

would do is go through and thoughtfully

8:00

answer each of these questions in turn.

8:03

But since I'm doing a video and this is

8:04

slightly artificial, I'm going to say,

8:06

"Could you just choose your recommended

8:08

answers for each of these questions?"

8:10

And that should speed us through to

8:11

actually making the change or

8:13

potentially creating an issue out of

8:14

this. So, it's now coming back with a

8:16

proposed module shape, and it's also

8:18

asking to verify a particular part of

8:20

the implementation where end is

8:22

collapsed and to sketch the actual

8:24

TypeScript interface. Yeah, go ahead and

8:25

do both. That sounds great. Let's ping

8:27

that off and see what it says. Okay, it

8:29

has figured out the implementation

8:31

detail it needed, and it's come back and

8:33

proposed a design here. So, which of

8:35

these functions are going to be

8:36

essentially the uh the interface for

8:39

this module. And so, we can talk about

8:41

this with the AI and figure it out. It's

8:42

again come back with two design

8:44

decisions that it wants my feedback on.

8:46

And here, I think you've got the flavor

8:48

of how this skill works and the kind of

8:50

conversations that you end up having

8:52

with the AI based on this. If I want to

8:53

turn this into an issue that my AFK

8:55

agent picks up, I can use two PRD or two

8:59

issues here. And by the way, if you're

9:00

interested in these skills that I'm

9:02

talking about, then you should check out

9:03

this site here, which is linked below.

9:05

I'm going to be creating a real

9:06

documentation site for these skills, and

9:09

for now, I have a newsletter that you

9:10

can sign up to for the latest updates,

9:12

as well as tips and tricks and resources

9:14

for getting the most out of agents. The

9:16

thing that's important to notice here is

9:18

just how much this skill demands of you,

9:21

the user. This is not an AFK skill that

9:23

you can just sort of run and kind of

9:24

like uh just rely on to continually

9:27

improve your code base. This requires a

9:29

judgment call from you, the programmer

9:31

sitting above the LLM. I think of agents

9:34

as really, really good tactical

9:36

programmers. They're able to get on the

9:38

ground and make changes quickly. But

9:40

they need someone on the level above

9:42

them who is the strategic programmer.

9:44

And that's what this skill does. It

9:45

allows the sergeant to go and run around

9:47

the code base and look for potential

9:49

improvement

9:50

opportunities. But then you, the

9:52

general, have to go and actually make

9:54

the change and decide what's good for

9:56

the long-term health of the code base. I

9:57

recommend that you run this skill, you

9:59

know, every couple of days, really.

10:01

Especially in a code base that's

10:02

fast-moving, you're going to come up

10:04

with tons

10:05

for deepening the code base. And the

10:07

deeper you get those modules, the higher

10:09

leverage you're going to get out of

10:10

them. And leverage as well means

10:12

testing. If you have a set of really

10:14

nice clear seams in your code base, then

10:17

you're going to be able to write really

10:18

nice tests around those nice deep

10:20

modules. And the better your tests are,

10:22

the better the output from the agent is

10:24

going to be. One final thought here is

10:25

that lots of folks ask me how you would

10:28

get started by using AI in a legacy code

10:30

base. And a legacy code base is probably

10:32

going to have a lot of shallow modules.

10:34

It's I mean, we talk about legacy code

10:36

bases, what we really mean are bad code

10:38

bases. Code bases that are hard to make

10:40

changes in. And what you really need

10:42

before you start making changes in a

10:44

legacy code base is a harness around the

10:46

code base to make sure that your changes

10:48

don't mess anything up. So for that, you

10:50

need tests, test, testing really nice

10:53

deep modules that have a lot of leverage

10:55

and locality. So running improve code

10:57

base architecture is a great place to

10:59

start. Thanks for watching, folks, and I

11:00

hope that answers some of your questions

11:02

about how to solve this never-ending

11:05

problem of AI just running away and

11:07

creating terrible code bases. I hope you

11:08

enjoy the skills. Do follow the link

11:10

below if you want to find more of them.

11:12

So thanks for watching and I'll see you

11:13

in the next one. And what you really

11:14

need before you start making changes in

11:17

a legacy code bases

Interactive Summary

The video discusses how AI is accelerating software entropy, leading to disorganized codebases, and provides a framework to reverse this trend. The speaker introduces 'deep modules'—units of code with simple interfaces and complex internal implementations—and explains how to use an 'improved codebase architecture' skill to refactor existing code. By identifying 'seams' and creating 'adapters', developers can improve code locality and leverage. The speaker emphasizes that while AI agents can act as tactical programmers, human oversight remains essential for strategic decision-making to maintain long-term codebase health.

Suggested questions

3 ready-made prompts