Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

Evaluating and Improving the Quality of Your Code as a Solo Developer

blog/cards/evaluating-and-improving-the-quality-of-your-code-as-a-solo-developer.jpg

Here's a number of questions you can ask yourself and tools you can use to help improve the quality of your code.

Quick Jump: What is Good? | Just Keep Building

Recently, I received an email from someone who signed up to my Flask course which said:

What I am weak on is writing good code and writing production apps. I am now in the industry and my team doesn’t have a senior engineer keeping me accountable with writing good code. That’s a main reason why I bought your course!

He also finished the email off with:

I wonder how indie devs get good at app building without being on a developer team.

I enjoyed this question because it forced me to think about the questions and steps I take to try and write good code. I’m not saying I write the best code (far from it) but there’s a number of things that go through my head while writing code as a solo developer and that’s what this post is going to focus on.

So here’s everything I’ve learned from being a solo freelance developer for the last 20 years.

What is Good?

There’s a fine balance between being able to move fast and creating a high quality code base. Depending on what your goals are, you may end up finding yourself skimping out on certain things for the sake of adding features quickly.

I don’t think that’s a terrible thing because honestly, if an app takes you 3x longer to create because you put the “burden” of perfection on your code base from day 1, that could end up making your app worse because it could gain less traction if it hasn’t shipped.

So you need to actively balance shipping and code quality. Fortunately there’s a number of tactics and tools you can use to help automate a lot of things which we’ll get into below.

Here’s a couple of questions I ask myself in no special order:

Does It Work?

Technically this one is first because I think it’s the most important one because having your app do what it’s supposed to do is a pretty decent way to measure goodness.

For example, if your app is supposed to process form submissions and currently every time you submit a form you get an error then it’s not working, so you should probably fix that.

Super obvious but very important!

Does It Make Me Happy When I Look at This Code?

This is pretty important to me and it’s also very subjective. I typically write Python and Elixir code nowadays but I wrote a lot of Ruby in the past and that community really drives home the importance of having visually appealing code.

And this can definitely be applied to other languages. This is something I spend a decent amount of time on. The tactics to do this will depend on what you consider good, but I typically focus on things like variable or function names, indentation levels and keeping a pulse on function length.

For example, looking at 3 small functions with little indentation is much more visually appealing to me than 1 large function with a few levels of indentation.

I personally find it much easier to skim and understand code in bite size chunks.

What’s nice about this one is it doesn’t take a huge amount of effort to keep things like this in check early on. I’ve never attempted to measure exactly how long it takes to do a refactor job across the scope of an entire project while doing a bunch of small refactors along the way but I would guess it’s a very good bang for your buck in terms of time spent and future maintainability.

How Hard Is This Code to Refactor?

This sort of stems back to happiness levels. Larger functions with many conditions tend to be harder to understand. If it’s harder to understand then it’s going to be harder to change.

Too much duplication can make things a bit harder to change as well. You should likely start off by duplicating things a few times but consider refactoring some of that out when you have a clear path forward. There’s no exact answer to this. It’s one of those things you’ll notice when the time comes.

If it takes you 3 months to build an MVP but it ends up being something you run for 3+ years, then having a painless path towards modifying the code you write is very important.

How Hard Would It Be to Add Features to This Chunk of Code?

I think it’s potentially dangerous to spend a lot of time wondering on the “what if” scenarios.

For example, putting a huge amount of effort on how to design a flexible library before you even wrote a line of code is going to be pretty bad. I’ve written about similar topics in an article about microservices.

It’s also very possible to make your code more convoluted and harder to reason about while trying to go abstraction crazy early on.

But at the same time, if you can pull off a design pattern that makes it easy to extend your existing code, by all means go for it. I think it’s important to at least think about this while coding something. If there’s low hanging fruit, grab it hand over fist.

As for how to do this, it depends. Generally I prefer to avoid things like class inheritance and other design patterns that require jumping through hoops to do certain things and generally makes it harder to trace your code.

The cliche answer is to consider keeping things simple. For example, early on I’m totally cool with passing around a function argument instead of trying to generalize something before I fully understand what I may want to do later.

If Someone Else Were to Look at This Code, Would They Get It?

I think this is a pretty important thing to think about even if you don’t plan for others to see your code. For example, if you’re writing some code that you would be embarrassed to show someone else, maybe it’s worth spending a little bit of time cleaning it up.

This doesn’t need to happen all at once though. I typically try to get something working as soon as possible, even if it’s the worst code ever written by a human being.

Then, after I get a proper test suite up and running, I’ll go back and modify it. That working but hideously bad code might end up staying in that state for hours or even days.

There’s a common phrase around this topic which is make it work, make it right and make it fast. I think that’s a great phrase to keep around in your mind, especially the first 2 parts.

Does This Code Run Fast and Use Minimal Resources?

Between SSDs, cheap memory and decently fast CPUs a typical web app written in most popular languages and frameworks will be quite nice. Of course some languages are more efficient than others but I don’t think it’s worth obsessing over things like this too much, but I do think it’s very much worth it to not ignore performance.

I would for sure spend a bit of time looking into hot spots of your code base. For example, if you need to do something every 10 seconds and this something currently takes 2 seconds to execute, but with a bit of profiling and research you can bring it down to 200ms, that seems like a worthy investment of time.

I basically keep performance and resource utilization in the back of my mind while coding everything but I don’t go as far as doing in depth research for every line of code.

For a first pass I try to keep things pretty high level like avoiding N + 1 queries, generally minimizing database calls when I can and moving uncontrollable or long running tasks into background jobs or async functions.

I also spend a good amount of time overlooking my database schema to ensure I have good indexes since they can make a world of a difference. For more in depth DB queries I tend to analyze them just to see if there’s any room for easy improvements.

Will I Remember What This Code Does in 2 Weeks?

If I ever start to write something complex, I constantly think about what will happen in a few weeks. If I can’t convince myself that I’ll remember what something does, I try to break down the problem even more and also break down the code base into more simple constructs.

If all else fails, then I’ll also write heaps of documentation. Here’s an example from an invoice related Bash script I wrote:

# [h: ] uses the "h", ":" or " " character as a split delimiter.
#
# If the first split argument ends with "m", then we're not dealing with
# hours at all, only minutes, so now $1 becomes minutes and we divide by
# 60 because you can't have more than 60 minutes in 1 hour.
#
# Otherwise, we have both hours ($1) and minutes ($2). The hours can be
# taken as is, and like before the minutes get divided by 60.
#
# It's also important to send both the hours and minutes in without a
# space as input, otherwise values like "30m" will end up being "30m "
# which makes $1 no longer end with an "m" and it will incorrectly report
# back 30.00 instead of 0.5.
#
# awk will automatically remove the "h" and "m" characters when it comes
# time to do the addition.
awk -F '[h: ]' '{ if ($1 ~ /m$/) printf("%.2f\n", ($1 / 60));
        else printf("%.2f\n", $1 + ($2 / 60)) }' <<< "${hours}${minutes}"

If the code isn’t understandable and it has no documentation then I don’t consider it good.

Am I Using the Same Coding Style across This Project?

I think this one is super important and it’s really easy to enforce because a number of languages have official style guides, community accepted style guides, static analysis / linting tools to help you enforce these rules and even built in functionality in some languages to keep things consistent.

IMO there’s really no excuse not to use these tools because it makes a huge difference in the long run. It’s one of those things where it takes a tiny bit of effort up front to set up but very little effort to keep your code in shape.

As for specific tools and guides. That will depend on which language you’re using. I recommend Googling for things like “style guides for X”, “linting tools for X”, and “static analysis tools for X” where X is your programming language of choice.

If I change something, will everything else still work?

If you change a bit of code in 1 spot, you really need a high confidence level that other unrelated code won’t break. I would say that if you don’t have a high confidence level, then your code is bad because it means it’s not going to be maintainable in the future.

Needless to say a strong test suite is the actionable step here to gain confidence in your code base. This could a mix of unit tests, all the way to end to end tests.

There is no magic bullet on what to test and how much test coverage you should have. It’s really a matter of how much confidence you want to have, but I do think for most web applications you will reach a point of diminishing returns at a certain point.

Personally I tend to hover around 80-90% overall test coverage in most projects but the number itself really doesn’t matter. It’s ensuring the important bits of your code base are well tested.

Tests are really important for being able to refactor code too. Whether or not you want to use TDD is up to you. Both styles are completely reasonable. Personally, I lean towards writing tests after I write the code but it depends.

If you’re curious, I made a video where I live coded how to begin writing tests in an untested code base.

Just Keep Building

To wrap things up, besides putting all of the above into use, I think the quickest way to improve is to keep building things.

If you sit down and start cranking out little side projects based on things that interest you, you will be writing code in the process and figuring out all sorts of interesting things along the way.

One of the reasons that this works is while you’re writing code, you’re constantly exposing yourself to new problems, new pain points and new patterns. You’re practicing while you’re building! That’s one reason why building is much more effective than just passively reading.

What are your favorite tips for writing good code? Let me know below.

Never Miss a Tip, Trick or Tutorial

Like you, I'm super protective of my inbox, so don't worry about getting spammed. You can expect a few emails per month (at most), and you can 1-click unsubscribe at any time. See what else you'll get too.



Comments