What Being a Tech Lead Taught Me About Writing Better Code

by Arif Ikhsanudin, Backend Developer

The fastest way to understand what makes code good or bad is to become responsible for code you didn't write. Tech leading changed how I write permanently.

Suddenly Other People's Code Was My Problem

The transition from individual contributor to tech lead comes with a particular kind of discomfort: you are now responsible for the quality of code you didn't write, don't fully understand, and can't always find time to fix. You also spend enough time in other people's code that your opinions about what makes code good get tested against reality at scale.

What I thought "clean code" meant before: well-formatted, reasonably commented, following the style guide.

What I understood it to mean after six months of tech leading: code that a tired developer can safely modify at 11 PM under pressure without causing a new incident.

That framing changed everything about how I write.

The Reader Is Not You

When you write code alone, you're also its main reader. You know what you meant. You know which edge cases you considered and which you punted on. The names you chose make sense because you were there when you chose them.

When you become responsible for a codebase written by a team, you experience what it's like to be a reader who wasn't there. Suddenly the things you used to do in your own code — the slightly opaque variable name, the function that does two things, the comment that says "fix later" — become friction you feel viscerally.

Write for the person who picks this up at 2 AM. That person is frantic, undertired, and trying to find exactly one thing as fast as possible. Are they going to understand what this function does from its name? Are they going to find the edge case handler, or are they going to miss it and cause an outage?

Code review from a tech lead perspective forced me to ask those questions about my own code, not just other people's.

Small Functions Aren't About Style

I used to think the preference for small, single-purpose functions was partly aesthetic — it was the "proper" way to write code, recommended in books, favored by linters. Something you did to be a good citizen.

As a tech lead reviewing PRs and onboarding new engineers, I understood it differently. Small functions are navigational aids. When every function does one thing and is named for that thing, a new developer can find their way through an unfamiliar codebase by reading names. They don't have to open every function body to understand what's happening at the call site.

Large, multipurpose functions invert this. Every call site is a black box. Understanding what changes where requires reading bodies, not names. The cognitive cost is proportional to team size.

I now write small functions not because I was told to, but because I've watched what happens when you don't.

Error Handling Is Architecture

As an individual contributor, I handled errors where it was convenient. Try-catch at the point of failure, generic exception rethrown up the stack, maybe a log line.

As a tech lead dealing with production incidents, I started to see error handling as a design concern, not an implementation detail. The questions that matter:

  • Where in the call chain does an error belong?
  • What information does the caller need to handle it appropriately?
  • What information does the operator need to diagnose it?
  • What's the failure mode — does the system fail loudly or silently?

Silent failures are the worst. A system that swallows errors and returns bad data is harder to debug than one that crashes dramatically. At least a crash produces a log.

I now think about error propagation the same way I think about data flow. It's a first-class design decision with architectural implications, not something to figure out after the happy path is working.

The Test Is a Specification

Before tech leading, I wrote tests to verify behavior. After, I started to see them differently: a test is a specification of what the code is supposed to do, written in a form the next engineer can run.

When I onboarded someone to a service I'd built, the tests were their map. If the tests were shallow or sparse, the new engineer had no way to understand what guarantees the system made. If the tests were comprehensive and well-named, they could understand the service's contract without reading all the source.

A well-named test is documentation that doesn't go stale. returns_empty_list_when_no_matching_records_found() tells the reader something concrete and checkable. test_query() does not.

I now write tests before or alongside code, and I name them as specifications: what does this system guarantee? Under what conditions? What are the edge cases it explicitly handles?

Simplicity Is a Team Property

The insight that changed my writing the most: simplicity isn't about you. It's about the team.

Code that's simple for an experienced developer on a Wednesday afternoon might not be simple for a junior developer on a Monday morning or anyone at 2 AM. The goal isn't code that's intellectually minimal — it's code that the weakest link in the team can safely modify without guidance.

That's a higher bar than it sounds. And meeting it consistently requires actively resisting cleverness, resisting abstraction for its own sake, and resisting the urge to optimize before you have evidence that optimization is needed.

The best compliment I've received on code: "This was easy to change."


You don't fully understand what your code costs until someone else has to maintain it — and by then, you can't take it back.

Scale Your Backend - Need an Experienced Backend Developer?

We provide backend engineers who join your team as contractors to help build, improve, and scale your backend systems.

We focus on clean backend design, clear documentation, and systems that remain reliable as products grow. Our goal is to strengthen your team and deliver backend systems that are easy to operate and maintain.

We work from our own development environments and support teams across US, EU, and APAC timezones. Our workflow emphasizes documentation and asynchronous collaboration to keep development efficient and focused.

  • Production Backend Experience. Experience building and maintaining backend systems, APIs, and databases used in production.
  • Scalable Architecture. Design backend systems that stay reliable as your product and traffic grow.
  • Contractor Friendly. Flexible engagement for short projects, long-term support, or extra help during releases.
  • Focus on Backend Reliability. Improve API performance, database stability, and overall backend reliability.
  • Documentation-Driven Development. Development guided by clear documentation so teams stay aligned and work efficiently.
  • Domain-Driven Design. Design backend systems around real business processes and product needs.

Tell us about your project

Our offices

  • Copenhagen
    1 Carlsberg Gate
    1260, København, Denmark
  • Magelang
    12 Jalan Bligo
    56485, Magelang, Indonesia

More articles

The Hidden Work Developers Do That Clients Rarely See

Clients see features appear, but they rarely see the effort behind them. What looks like “instant delivery” is often hundreds of invisible decisions and hours of work.

Read more

Tagging Releases in Git Is Not Optional in a Real Project

Tags are the permanent bookmarks in your commit history that mark what was deployed, when, and what version it was. Without them, debugging production incidents and understanding what your users are running becomes guesswork.

Read more

LA's Tech Scene Is Everywhere and Nowhere — Why Backend Hiring Is Harder Than It Looks

Los Angeles has more tech activity than most people realize. That doesn't make finding a solid backend developer any easier.

Read more

Deadlocks in Java — How They Form, How to Find Them, and How to Design Around Them

Deadlocks are deterministic — given the same lock acquisition order and timing, they reproduce reliably. Understanding the four conditions that create them makes both prevention and diagnosis systematic rather than guesswork.

Read more