Developer experience is what building software feels like from the inside. I think of it like UX, except the engineer is the user. The codebase, tools, workflows, and team habits all shape that experience.
The shortest path to a safe change
My test for code quality is simple: can another engineer find where a change belongs, understand the intent, and verify it without reconstructing the system?
Folders named utils, helpers, or services aren't automatically bad. They become a problem when they turn into dumping grounds. I'd rather see names that describe work an engineer can recognize at a glance.
market_data/
├── ingestion/
│ └── fetch_exchange_rates.py
├── normalization/
│ └── normalize_quotes.py
└── publishing/
└── publish_snapshot.pyFast local checks save everyone time, but they're only the first pass. I want one command that catches the obvious stuff before I push. Pull-request CI, deployment checks, and production safeguards still need to prove the change works outside my laptop. That quick feedback loop makes experiments cheaper and catches mistakes before they slow someone else down.
Better defaults, one file at a time
At LinkedIn, I worked in a codebase with years of JavaScript behind it. Important product entities still reached the frontend as loosely shaped objects without a complete contract. Even when I understood a campaign, I had to log the object to see which fields and states were actually there. That guesswork made complex workflows harder to reason about and left room for bugs around the edges.
I helped lead our feature group's move toward TypeScript while regular product work continued. A full rewrite was never practical. When feature work took us into a legacy JavaScript file, we often converted it along the way. New features started with typed props, state, and API contracts from day one.
The migration needed patterns the group could copy. I helped shape and document them, then reinforced them in review. TypeScript moved assumptions into the code and changed the default within our team.
GraphQL adoption was organization-wide, and our team established the first mutation pattern for others to follow.
Over time, typed contracts became the normal starting point for the group. New features required less guesswork, and everyday product work kept moving the legacy code in the same direction.
Make the common path boring
The code is only part of the experience. A new engineer should be able to clone the repo, follow current setup instructions, and run the project without collecting secrets and shell commands from five people. Builds and focused tests need to be fast enough that engineers don't learn to avoid them.
When the same friction shows up across teams, another workaround is not much of a fix. A shared template, generated client, or internal platform can solve the problem once and improve the default for everyone. Those tools still need owners, useful errors, documentation, and a way to hear when the paved path no longer fits the work.
The loop also continues after merge. Release tooling should make it clear what broke and whether it can be rolled back. And observability should help connect a production problem to the change that caused it.
The codebase should explain itself
Code is good at showing what happens today. It rarely explains why a decision was made. I prefer idiomatic code first, comments when the “why” isn't obvious, and design docs for decisions too large to live beside one function. That keeps constraints and tradeoffs from disappearing into meetings.
Code review and onboarding show where context is missing. If the same explanation keeps coming up, it probably belongs near the code.
Engineering is complex enough on its own. The tools used to build the product shouldn't become a second product you have to fight.