4 min read

Good practices that actually matter

The ones that quietly determine whether a codebase is healthy at year three.

Everyone has a list of good engineering practices. Most lists are the same list: write tests, document your code, keep functions small, don't commit secrets.

That list is fine. It's also not sufficient. Here are the practices I've found actually determine whether a codebase is healthy when you come back to it two or three years later.

Name things for what they are, not what they do

A function called process_data could do anything. A function called extract_weight_readings_from_sensor_payload can only do one thing, and you know if it's doing something else.

This sounds obvious. It's not what I see in most codebases. The tendency is to use generic names while building fast because the specific name requires thinking about what the thing actually is. The cost is paid by everyone who reads the code afterward, including you in six months.

The same principle applies to variables, modules, services. Generic names are a debt you take on at authoring time and pay at reading time. You'll read the code more times than you write it.

Errors should be loud by default

Silent failures accumulate. A function that returns None instead of raising an exception, a service that continues running after a configuration error, a pipeline that processes partial results without flagging what it skipped - these fail in ways that are easy to miss until the consequence surfaces somewhere far from the cause.

The discipline: when something goes wrong, make noise. Raise exceptions rather than returning sentinel values. Log at error level rather than swallowing. Fail fast rather than continuing with degraded state.

The exception (literally): known, expected, and recoverable failure conditions. These should be handled explicitly and silently. The discipline is being intentional about which category you're in.

Preserve the why alongside the what

Code tells you what happened. It doesn't tell you why decisions were made. When you inherit a codebase with a seemingly arbitrary constraint - this service must not write to the database during request handling, this model can only be called with batch sizes under 32 - you either trust it and work around it, or you remove it and find out why it existed.

Decisions that look arbitrary usually have a reason. Document the reason at the time, not afterward.

We use short decision records in the repository for non-obvious architectural choices: what we considered, what we decided, and why. Not every decision warrants this. The test: if a new engineer would look at this and ask "why is it done this way?", write it down.

Keep the deployment path boring

Every step in the deployment process that requires manual judgment or human intervention is a failure point. Not because humans make mistakes, but because judgment under pressure is worse than judgment at leisure, and deployments often happen under pressure.

We've worked toward a state where a deployment is: run the pipeline, watch the metrics for fifteen minutes, done. No checklist. No manual steps. No decisions to make mid-process.

Getting there required moving decisions earlier (into code review, into staging) and automating the things that used to require human judgment in production. It took two years. A boring deployment process is worth it.

Treat operational work like product work

The runbook that takes an hour to follow should be a twenty-minute runbook or an automated job. The alert that fires once a week and always resolves itself should be tuned or removed. The manual data migration that runs quarterly should be automated.

Operational tasks feel less important than product tasks because they don't ship features. They determine whether the product keeps working. The teams that treat operational work as second-class produce systems that work well when conditions are good and fall apart under pressure.

The actual test

A codebase is healthy if an engineer who joins the team can: understand what a system does by reading it, understand why it's built the way it is from the documentation, deploy a change without fear, and respond to an incident at 2am without calling anyone.

Most practices are good. These are the ones I've found make the difference between a codebase that ages well and one that doesn't.

With gusto, Fatih.