The habits, tools, and practices that set great engineering teams apart

Developer Experience (DevEx) isn’t just about fancy tools or slick UIs — it is about removing friction so teams can move with confidence, speed, and clarity. In high-performing teams, great DevEx means fewer context switches, faster feedback loops, and more time spent actually building. In this blog, we’ll explore the five non-negotiables every codebase should have to support world-class collaboration, and we’ll map out a practical DevEx stack to help your team deliver better products, faster.

The Five Non-negotiables

I. Project readme

Short, sweet and simple

Write a short note with a few lines on what this codebase is responsible for. Indicate the setup process and lifecycle to go to production. Code should act as documentation and anything that code will not document as obviously (what are the first set of things you should read) should be in here.

II. Automated setup

A single command to get your entire workstation setup

I am a huge fan of using shell scripts for smaller projects and justfiles for larger ones. This isn’t about tools. This is about your experience.

Run just setup and have a workstation that is ready to go (including requiring node/python, installing all the dependencies and setting up a database, if required. I expect other obvious commands like just run, just lint, just test and just build. I admit that I have been spoiled by gradle and maven in JVM land and clearly have withdrawal symptoms in the land of the snakes.

Take this a step further and automate test data creation. If your application is stateful, please generate the test data on startup. This way, you are ready to test what you need the moment your application starts. Test data setup might add a few seconds to your startup but it will save you minutes in testing things and much more than that in your emotional happiness. If you are building an e-commerce website, create a few product categories, products in each of the categories and a few test users. Make sure your test user has elevated privileges to begin with making it easier for you to start testing things. The single just run command should have you ready to test your scenarios.

III. Iterate fast

Faster the feedback, the better

I like fast iterations. Left on my own, I’d commit every 5-10 minutes; sooner, if I can get away with it. This includes the time it takes me to lint and test. This means fast code linting and tests. I love code linting tools that take less than a second and unit tests that take less than 5 seconds across the entire project. If running all tests takes more than 5 seconds, I’ll run them before a push. If it takes more than a minute, I’m refactoring/optimising something.

There are enough engineering techniques to go fast. Got a large number of tests? Run them in parallel. Integration tests take time? Share container context and database test containers.

Once you get used to this, you will not go back.

IV. Enforced pre-commit/pre-push checks

Shift feedback leftward

Use frameworks like pre-commit (others exist for most toolchains) to run your entire CI safety net locally. Early feedback is key. Lint code with every commit. Linters should spot issues (like increased complexity, dead code, etc.) early and format code consistently. Test everything before pushing.

V. Everything runs locally

Nothing should require the internet or external resources, if possible

Can your run your code locally? Sounds like a silly suggestion but I bet that there is at least one team still working with an old system that’s written in C that logs into a remote machine and writes code in vim without any setup for code completion, early compilation feedback or running code in an “IDE like setup” with world-class debugging support.

Use a proper IDE. I personally love the IntelliJ suite of tools for most languages. Some of my teammates are emacs and vim power users who have the same setups (code completion, auto-compilation, error detection, running code, and debugging support). IntelliJ even comes with its own set of profiling tools that are a real timesaver for me and easily worth the cost of usage.

A teammate once asked “If you were to get on a flight, could you continue to write code”. This was not a hypothetical question as we used to travel every week and spend 3+ hours on a flight, time you’d like to make good use of. Having a toolset that you can go offline and work comfortably in, even when travelling is a really nice experience as a developer.

The DevEx Stack

Want to go beyond the non negotiable items and dive deeper into improving your team’s DevEx? Here’s is a stack with some techniques, tools and practices to try out.

This section is going to be heavy with crosslinks to other articles to keep this article short for people who already know some of these concepts.

Layer

Tools/Practices

Code Quality

Linters, Formatters, Typing, Modular Design

Automation

Pre-commit, CI/CD, Makefiles, Containerisation

Testing and Validation

Fast tests, Coverage, Contracts, Security Scans

Documentation

Onboarding, Readmes, ADRs, Comments, PR Templates

Culture and Workflow

Git hygiene, Blameless retros, Tech debt tracking

Foundational code practices

People have their preference in how code is styled and a good codebase is one that looks like a single person has written it. Have a clear and consistent code style that is enforced via linters and formatters that work across the CLI and IDEs that the team uses. Use a configuration that is checked into version control to ensure consistency.

Duck typing enthusiasts can look away but please prefer strong typing (Typescript over Javascript, mypy on Python etc.). Your IDE suggestions (code completions) and ease of exploration of language APIs will thank you, especially if your team aren’t experts at the language.

Build a codebase that has clean code architecture (layered, hexagonal, etc.). The codebase should clearly showcase design preferences (composition over inheritance) and even codify them through tests or fitness functions when possible.

When the code isn’t obvious, do not add comments. Write better tests and refactor your code.

Tooling and automation

Run formatters, linters, and tests automatically (using tools like pre-commit and husky). Build CI/CD pipelines that are fast and reliable which provide meaningful feedback when things fail. Automatic deployments to non-prod environments. Automated rollbacks strategies when deploying to production. It’s 2025 and there are very few reasons to need downtime even when running most standard migrations. Build observability into your pipelines to help diagnose issues (like pipelines slowing down) quicker.

A local developer experience that is consistent with production (docker, docker compose, and vagrant environments for more bespoke operating systems). Use scripts for common workflows (just, npm scripts).

Builds need to be deterministic and reproducible. Lock your dependencies and avoid dependency hell. This might not be a big deal to the experience of developers on a daily basis but add periodic checks for outdated or vulnerable dependencies (using tools like snyk).

Testing and verification

Automate your tests with good quality unit tests for your logic, integration tests for the boundaries and (hopefully consumer driven) contract tests for external APIs that together, make up a good test pyramid or test trophy. Do not chase test coverage numbers. Use coverage to catch critical paths that are not well tested. Flaky tests suck, please eliminate them like a plague. Make them easy to run using an obvious command (like just test, npm test or ./gradlew test).

Use test doubles when necessary. Mocks and stubs are required but try to be stateful when possible (the last bit is a debatable opinion; one of the few endless debates in this blog). Use snapshot tests when possible but do not abuse this technique.

Lint your code. Do so early. Add security linters (like bandit or semgrep).

Collaboration and documentation

Every project should have a README.md, a TL;DR of your quick start guide for developers. Add a CONTRIBUTION.md for guidelines on how people can be good contributors (do you practice trunk based development or git flow? The answer will not be obvious to everyone when starting off on the project). Set up PR templates and code review guidelines to help aid internal conversations.

Automate your setup. New developers on your team should be productive in less than 5 minutes of the repository checkout (including the time taken to download dependencies).

If you’re creating an SDK or API, please generate auto-generated documentation. Capture decisions as Architectural Decision Records (ADRs) and C4 diagrams. This makes continued context maintenance and acquiring historical context easier.

Team workflows and culture

Decide on Trunk Based Development (TBD) or Git Flow (GF). If you’re going with TBD, merge early and merge often but do so with feature toggles. If you’re going with GF, create short-lived feature branches.

Setup a culture of blameless retros to learn from your mistakes effectively.

Track tech debt actively on the backlog and manage it regularly. Acknowledge and prioritise debt along side features.

Ensure the team is used to sharing feedback openly. Set up retrospectives as a group and time to introspect as individuals.


This might all sound obvious in hindsight . So, why doesn’t every team invest in it? In truth, many developers have never experienced what great DevEx feels like. They don’t know it can be better, or they’ve accepted the friction as normal. But once you’ve worked in an environment where someone has sweated the details — where every part of the workflow feels seamless — you can’t unsee it. You start to expect it. And that expectation changes everything.

What’s next?

Every team deserves a developer experience that brings out their best work. Start by imagining what “great” looks like for your codebase — your north star. Then chart a course. Build a roadmap. Rally others. The path from chaos to clarity is paved with small, deliberate steps.

Take 10 minutes today to write down your team’s DevEx wishlist. Start a conversation with the team: What’s slowing us down? Pick one thing from the DevEx stack and implement it this week.

Change starts with you.

In the next blog, we’ll dive into how AI coding assistants can help amplify your impact — accelerating code quality, catching issues early, and automating the boring stuff — so you can focus on what really matters: building things that matter.


Credits

Thanks to Vinayak Kadam for providing feedback and Priyadarshan Patil for requesting me to write about this, after my passionate filled monologue in a conversation about Developer Experience.