The Jump From Writing Features to Thinking in Systems

by Eric Hanson, Backend Developer at Clean Systems Consulting

The Ceiling Nobody Labels

There's a ceiling in most engineers' careers that doesn't look like a ceiling from the inside. You're shipping consistently. Your code quality is good. You're trusted with increasingly complex features. And then you get feedback that you're "not quite senior yet" and you can't fully decode what that means.

What it usually means is this: you're still thinking primarily in features, not in systems. You zoom in on the work in front of you with exceptional focus and skill. What you're not yet doing — reliably, automatically — is zooming out to see how the component you're building fits into everything around it, what it does to the system when it changes, and what the system needs from it that the ticket doesn't say.

This is the actual jump. It's not about knowing more things. It's about changing what you look at.

What Feature Thinking Looks Like

Feature thinking is optimized for the unit of work. The question is: how do I build this thing correctly?

You read the requirements. You understand the expected behavior. You write code that implements the behavior. You write tests that verify the behavior. You ship.

This is excellent work. Most software requires it. The problem is that it's incomplete as a way of thinking about complex systems.

Feature thinking treats the inputs and outputs of a component as fixed: the requirements tell you what they are. It treats the system context as a boundary condition, not an object of attention. It focuses on correctness within the defined scope and treats everything outside the scope as someone else's concern.

What Systems Thinking Looks Like

Systems thinking asks a different set of questions before, during, and after building:

Before: What existing parts of the system does this change? What assumptions does it make about their behavior? What assumptions do they make about the behavior of the component I'm building?

During: If this component fails or behaves unexpectedly, what does that do to everything downstream? What data flows through this path and what are its consistency requirements? Is this component observable — can someone understand what it's doing from logs and metrics without reading the code?

After: If requirements change in the most likely directions, which parts of this design need to change? Where are the seams? What would make this hard to modify?

The feature question is "does this work?" The systems question is "what does this do to the system?"

The Concrete Difference

A feature-thinking engineer builds a new background job that sends weekly digest emails to users. The job queries users, filters for those with digest preferences enabled, renders emails, and sends them. It works. All the tests pass.

A systems-thinking engineer builds the same job and also asks:

  • What happens when this job runs concurrently with itself? (Distributed lock or uniqueness constraint on sent_at timestamp)
  • What happens when the email service is down? (DLQ or retry table)
  • How do we know if it's silently failing for some users? (Per-job success metrics and alerting on below-expected send counts)
  • What happens at 10x the current user volume? (Batching, pagination, index on the user query)
  • How does someone disable this for a specific user in production without a deploy? (Feature flag or per-user suppression)

None of these are in the ticket. All of them are in the job of building a system.

The Practices That Develop Systems Thinking

Own things across their full lifecycle. Building a feature teaches you about implementation. Being on-call for it teaches you about operations. Investigating its first production incident teaches you about failure modes you didn't anticipate. Engineers who only experience the build phase develop feature thinking. Engineers who live with what they built develop systems thinking.

Read post-mortems actively. Not to see what went wrong, but to see what assumptions the original builder made that turned out to be wrong. Every incident is a case study in the gap between the system as designed and the system as it behaves.

Draw the system before you design the component. Before writing any code for a significant feature, draw the existing system and identify where the new component connects. This forces you to see the connections explicitly rather than treating them as context you'll deal with later.

Ask "what breaks" before "how do I build." Make it a ritual. Before writing the first line of implementation code, spend ten minutes listing the ways the thing you're about to build could fail or misbehave. Then design to address the most likely and most costly failures.

The Practical Takeaway

Choose one piece of work you've shipped in the last three months. Map every other system component it depends on and every component that depends on it. For each connection, write down: what happens if that dependency is unavailable? What assumptions does this component make about the data it receives? If you find gaps in your original design, you've just done the work of systems thinking retroactively. The goal is to do it prospectively next time.

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

Essential Tools Every Backend Contractor Needs

“I just need a laptop and I’m good to go.” That’s what most backend contractors think—until the real work starts. The truth is, your tools shape your speed, your quality, and even your reputation.

Read more

Why Melbourne Startups Cannot Win on Local Backend Hiring Alone

Melbourne has genuine tech depth and a startup scene worth taking seriously. Local backend hiring alone isn't enough to keep a growing product moving.

Read more

How to Roll Back a Production Catastrophe Without Panic

Production disasters happen, often when you least expect them. Knowing how to roll back calmly can save hours of stress and downtime.

Read more

How I Manage My Energy as a Developer, Not Just My Time

Time management systems assume all hours are equal. They aren't — and once you start treating your energy as the scarce resource instead of your calendar, the way you work changes considerably.

Read more