Live data from Hacker News

Code doesn’t have to be a mess

danielsieger.com

21–30 of 190 posts

Re: Code doesn’t have to be a mess

#21
post #2

Would be curious to know what strategies other people apply in order to keep complexity down over time!

I like a lot of your other replies. I also have a philosophy of doing net improvement every time I go in. If you put a little bit of elbow grease in every time, the net effect on your code over months is pretty nice.

But you also have to understand and internalize that it's OK to do a little bit of improvement each time. You don't have to go in, pick up a piece of code, sigh dramatically, and fix everything you can see about it. Just fix a bit. Turn some strings into enumerations or a custom type. Turn a recurring series of arguments into a single struct. Rename a deceptively-name parameter or function variable into something correct and meaningful. Add a test case for what you just did, or add a test case for something even related to what you just did that was not previously covered. Even just one of those is a good thing. Don't give in to the temptation to throw a 15th parameter on to a function and add another crappy if statement in to the pile of the god function. Don't fix the god function all at once, just take a bit back out of it.

If every interaction on the code base is net positive, even just a bit, over time it does slowly get nicer, and if you greenfield something with this attitude, it tends to stay pretty nice. Not necessarily pristine. Not necessarily nice in every last corner. But pretty nice. And if you do need to take out some technical debt, you'll have the metaphorical capital with which to do it; a non-trivial part of the reason why technical debt has such a bad rap is that it is taken out on code bases already bereft of technical capital, which means you're on the really bad part of the compounding costs curve to start with.

Re: Code doesn’t have to be a mess

#22
The part about constraints is kind of muddled. Constraining the scope of your project is not the same thing as working within a set of externally imposed constraints, which is what people are usually referring to in stories about how being forced to do more with less led to an unexpected innovation of some kind. The former is really just defining the scope of the project, which is covered in the following section.

Re: Code doesn’t have to be a mess

#23
post #8

Ah the Unix philosophy. `man ssh' gives `ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-J destination] [-L address] [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] destina…

Wasn't the "Unix philosophy" explicitly formulated by Rob Kernighan in 1983 in opposition to this kind of growth? I mean, there's a whole website of Unix purists named after it:

'UNIX Style, or cat -v Considered Harmful' http://harmful.cat-v.org/cat-v/

Re: Code doesn’t have to be a mess

#24
post #2

Would be curious to know what strategies other people apply in order to keep complexity down over time!

Unit Tests. If you can't write a unit test for it, it's too complicated and it's going to snowball quickly into a giant mess.

Be careful with this. Unit tests don't tell you much about the correctness of a system overall, and they rarely survive a substantial refactoring. Optimizing for unit testability can make individual classes/functions "simple" but at the expense of creating a ton of them and pushing the complexity to the interfaces and integration between them.

Re: Code doesn’t have to be a mess

#25
post #6

There's a great section in The Practice of Programming where the book describes how you should structure your code to not just be structured nicely now, but to plan for the future; to structure it so that changes are easy, organized, and don't break anything. It's not exhaustive but it's a powerful general idea and I always like introducing developers to it for the first time.

I must say here that I agree this point but one should also exercise caution that they don't go overboard with abstractions while planning for future. Abstractions for future planning should be lean and flexible enough to be extensible.

Sometimes called YAGNI, or You Ain't Gonna Need It.

I've found that the right level of abstraction is the one that saves time and effort and duplication now, for the features you're currently shipping. If you're thinking about hypothetical new features that aren't even on the roadmap then you've gone too far.

As an example, say your embedded program needs to load images, and your standard library only supports raw BMP files. BMP images are going to work great for the time being, since the art team can supply them that way. By all means, add abstraction method around the library method called "load_image" so that you don't have to refactor a million places to replace that library. It'll give you a great place to add error handling, logging, etc.

Beyond a single method to add a point of attack, don't go beyond that and spend the time to add a JPEG and PNG library, don't add support for high bit-depth images, or for grayscale images, or CMYK images. Don't add an abstraction that'll someday be able to load the Nth frame from a video file. Perhaps throw an exception for unusual input, but beyond that don't waste your time.

In my experience having simple, direct code makes it easier to refactor in the future when the need arises. Having too much abstraction or future proofing gets in the way because the future inevitably will bring different changes than what you were expecting.

Re: Code doesn’t have to be a mess

#26
> Say No

Getting junior devs to do this is like pulling teeth. Trying to get a feature stopped after they've built it is soul crushing for them. It's a problem.

At this point I've all but given up beyond minimizing the blast radius in code review.

Re: Code doesn’t have to be a mess

#27
post #2

Would be curious to know what strategies other people apply in order to keep complexity down over time!

I'm not a greybeard by any stretch, but I personally get a lot of mileage out of just stopping to ask: Does the extra layer of abstraction, or extraction of code to a method, or creation of a class - does it make the code *right now* easier to understand? If yes, do it, if not, don't.

The example I keep coming back to is when I was a junior, one of the other juniors refactored the database handling code in one of our apps to use a class hierarchy. "AbstractDatabaseConnection" "DatabaseConnection" etc. And mind you this was on top of the java.sql abstractions already present.

I don't necessarily know what his end goal was, since the code still seemed pretty tightly coupled to how java and postgres handle connections and do SQL. One might theoretically now be able to create a testing dummy connection that responds to sql calls and returns pre-baked data. But the functions we had were already refactored to be pure functions, and the IO was just IO with no business logic.

Anyway, all it ended up doing was making it so I never touched the database code in that app ever again. Integration testing was handled by just hooking it up to a test db via cli args and auto-clicking the UI. And eventually when people started side-stepping it, I took the opportunity (years later) to just go back in and replace both it and all the side-stepped code with plain ole java.sql stuff that literally anyone with two thumbs and 6 months of java experience could understand.

So now, unless I have some really strong plan (usually backed up with a prototype I used to plan out the abstraction) for an abstraction model, I just write code, extracting things where the small-scale abstractions improve current readability, and wait for bigger patterns (and business needs) to emerge before trying to clamp down on things with big prescriptive abstraction models.

Re: Code doesn’t have to be a mess

#28
> Minimize Dependencies ... Consider doing it yourself.

This is terrible advice!

Maximize your dependencies. Adopt as much external code as possible. Build what you can with it. Then, as you reach the limits of those dependencies, and you absolutely understand what needs to get done replace them as you need to.

The vast majority of what people write will be trashed and/or changed radically. You should adopt whatever tools are required to get things working minimally and then make decisions like this.

Re: Code doesn’t have to be a mess

#29

The part about constraints is kind of muddled. Constraining the scope of your project is not the same thing as working within a set of externally imposed constraints, which is what people are usually referring to in stories about how being forced to do more with less led to an unexpected innovation of some kind. The former is really just defining the scope of the project, which is covered in the following section.

I’ve had a few bosses give the speech about no heroes.

If I’ve given a speech, well there are several but the one relevant here is instead of trying to build the perfect product, building the best product we can build.

If you don’t follow that constraint you end up in Kernighan’s Law territory, and the wheels eventually come off.

Know your strengths. Build up or compliment your weaknesses, stop trying to Fake It Til You Make It when you’ve made it most of the way to where you’re going to get.

Re: Code doesn’t have to be a mess

#30

> Minimize Dependencies ... Consider doing it yourself. This is terrible advice! Maximize your dependencies. Adopt as much external code as possible. Build what you can with it. Then, as you reach the limits of those dependencies, and you absolutely understand what needs to get done replace them as you need to. The vast majority of what people write will be trashed and/or changed radically. You should adopt whatever…

When the dependency is deprecated, I have to stop what I'm doing and replace the dependency. If the dependency has a show-stopper bug, I either have to wait, vendor the dependency, or rewrite. That's what the original article advocates for: be careful what you import. leftpad, probably write it yourself. React, OK to use, but maybe vendor.
Post reply on HN