10 Skills Every Software Engineer Needs to Grow

The skills that move a software engineer from junior to trusted are mostly not new languages or frameworks. They’re the abilities that make you useful on any stack: debugging code you didn’t write, changing code safely, designing for real load, and communicating clearly enough that people can rely on you. Below are ten of them, seven technical and three not, each with a specific way to practice it this month.

The table is the quick view. The sections after it explain what “good” looks like for each skill and how to get there.

# Skill Type One way to start practicing
1 Debugging and reading unfamiliar code Technical Take bug tickets in parts of the codebase you don’t know
2 Writing code other people can change Technical Refactor an old project of yours and note what confused you
3 Automated testing Technical Write a failing test before every bug fix
4 Git beyond add, commit, push Technical Use git bisect to find the commit that introduced a bug
5 SQL and data modeling Technical Read the query plan for your app’s slowest query
6 System design basics Technical Write a one-page design before your next feature
7 Security fundamentals Technical Audit one project against two OWASP Top 10 categories
8 Clear writing Non-technical Use a fixed template for PR descriptions and status updates
9 Scoping and estimating work Non-technical Track your estimates against actual time for a month
10 Learning quickly, including with AI tools Non-technical Learn your next library from its official docs, then explain it to someone

1. Debugging and reading unfamiliar code

Most of your working time goes into code that already exists. Engineers who can open an unfamiliar module, form a hypothesis, and confirm it with evidence get trusted with harder problems quickly. Engineers who change things at random until the error disappears don’t.

Good debugging is a loop: reproduce the problem reliably, narrow down where it happens, form one hypothesis, test it, repeat. Reading code works the same way. Start from an entry point (a route, a screen, a command) and follow the path a single request takes through the system.

How to practice:

  • Learn your debugger properly: breakpoints, conditional breakpoints, stepping into calls, and inspecting variables. Print statements are fine, but they shouldn’t be your only tool.
  • Volunteer for bug tickets in parts of the codebase you’ve never touched.
  • Before fixing a bug, write down in one sentence what you think the cause is. Afterwards, check whether you were right. Over time this trains your intuition.
  • Read the source code of one library you use every day. Pick a function you call often and trace what it really does.

2. Writing code other people can change

Code gets read and modified far more often than it’s written. The test of good code isn’t cleverness. It’s whether a teammate can change it six months from now without breaking something.

In practice that means descriptive names, functions that do one thing, side effects kept at the edges, and comments that explain why rather than what. It also means knowing when not to abstract. Two similar blocks of code are often easier to change than one generic function with five configuration flags.

How to practice:

  • Go back to a project you wrote a year ago. Mark every spot where you struggled to understand your own code, then refactor those parts.
  • Put refactoring in separate commits or pull requests from behavior changes, so each one is easy to review.
  • Ask your reviewers directly: “Which part was hardest to follow?” The guide to code reviews for authors and reviewers covers how to get feedback that’s actually useful.

3. Automated testing

Tests are what let you change code with confidence. Without them, every change is a gamble, and teams slow down out of fear of breaking things. The standard mental model is the test pyramid, explained well in The Practical Test Pyramid on Martin Fowler’s site: many small, fast unit tests at the bottom, fewer integration tests in the middle, and very few end-to-end tests at the top.

The skill isn’t writing lots of tests. It’s writing tests that fail when behavior breaks and keep passing when you merely restructure the code. Tests tied to implementation details break on every refactor, and the team learns to ignore them.

How to practice:

  • For every bug you fix, first write a test that reproduces it and fails. Then fix the bug and watch the test pass.
  • Deliberately break a function you’ve tested. If no test fails, your tests aren’t protecting what you think they are.
  • Learn your stack’s tools for faking network calls and seeding a test database, so integration tests are cheap to write.

4. Git beyond add, commit, push

Every team uses version control, and engineers who understand Git beyond the basic commands recover from mistakes faster and leave a history that’s worth reading. You should be comfortable with branching, the difference between rebasing and merging, resolving conflicts, and undoing things safely.

How to practice:

  • Read the first three chapters of Pro Git, the free book by Scott Chacon and Ben Straub. The branching chapter alone clears up most of the confusion people carry for years.
  • Next time you need to find when a bug appeared, use git bisect instead of reading commit messages one by one.
  • Clean up a messy feature branch with an interactive rebase before you open the pull request. Do this only on your own branch, never on shared history.
  • Write commit messages that explain why the change was made, not only what changed.

5. SQL and data modeling

Frameworks come and go, but the data underneath usually outlives them. Many applications keep their most important data in a relational database, and many everyday performance problems come down to a missing index or a query that runs once per row inside a loop (the “N+1” problem).

You should be able to design tables for a new feature, write joins and aggregations by hand, explain what an index does and what it costs, and recognize when a document or key-value store would be a better fit.

How to practice:

  • Turn on query logging in your development environment and look at what your ORM actually sends to the database.
  • Run EXPLAIN (available in PostgreSQL and MySQL) on your slowest queries and learn to read the output.
  • Model the data for a feature on paper before writing code: the entities, how they relate, and which queries need to be fast.

6. System design basics

You don’t need to design a global-scale system to benefit from design thinking. Even a small feature raises design questions. Where does the state live? What happens when an external API is slow? What if the same request arrives twice? How does this behave when traffic grows tenfold?

Concepts worth knowing: caching, queues and background jobs, load balancing, replication, idempotency, and the trade-off between consistency and availability. The open-source System Design Primer on GitHub covers all of them, with diagrams and practice questions.

How to practice:

  • Before your next non-trivial feature, write a one-page design: the problem, your approach, the alternatives you rejected, and what could go wrong. Ask a senior engineer to poke holes in it.
  • Read the postmortems your company writes after incidents. They show exactly where real designs fail.
  • Pick an app you use daily, such as a chat or ride-hailing app, and sketch how you’d build its core feature. Then compare notes with a colleague.

7. Security fundamentals

Security isn’t only the security team’s job. Most vulnerabilities start as ordinary bugs: a missing permission check, a database query built from raw user input, a secret committed to a repository. The OWASP Top 10, now in its 2025 edition, is the standard awareness list of the most critical web application risks, and the right place to start. Broken Access Control sits at number one.

How to practice:

  • Pick two OWASP categories, such as Broken Access Control and Injection, and audit one of your own projects against them.
  • For every endpoint you write, answer two questions: who is allowed to call this, and where in the code is that checked?
  • Turn on dependency vulnerability alerts for your repositories, and actually work through them.
  • Check your logs for passwords, tokens, or personal data. If any appear, fix it.

8. Clear writing

A lot of engineering happens in writing: pull request descriptions, design docs, bug reports, status updates, and messages to colleagues in other time zones. On remote and distributed teams, your writing is often how people judge your thinking. A clear two-paragraph update can replace a meeting. A vague one creates three follow-up questions.

Good technical writing puts the conclusion first, says what you need from the reader, and gives enough context that someone outside the project can follow it.

How to practice:

  • Use a fixed structure for pull request descriptions: what changed, why, how to test it, and what’s risky.
  • Put a one-sentence summary at the top of any long message. If you can’t write that sentence, you’re not ready to send the message.
  • When you ask for help, include what you tried and what happened. You’ll get better answers faster.

A short status update in this shape works in almost any team chat:

Payment retries: on track for Thursday
Done: retry logic and tests merged
Next: admin screen for failed payments
Blocked: need sandbox API key from the payments team
Risk: provider rate limits may require a queue, checking Wednesday

9. Scoping and estimating work

Senior engineers aren’t trusted because they type faster. They’re trusted because when they say something will take a week, it usually does, and when it won’t, they say so early. That comes from breaking work into small pieces, tackling the unknowns first, and understanding the problem behind the ticket well enough to propose a smaller version that still solves it.

How to practice:

  • Split every task into pieces of a day or less before estimating. Anything bigger is hiding unknowns.
  • Do the riskiest part first: the unfamiliar API, the data migration, the performance question.
  • For one month, record your estimate and the actual time for each task. Look for your pattern, for example consistently underestimating integration work or testing.
  • When a deadline is fixed, offer a scope trade-off rather than a silent overtime plan: “We can ship X by Friday, or X and Y by next Wednesday.”

10. Learning quickly, including with AI tools

The stack you use today probably won’t be the one you use in five years. The durable skill is picking up a new tool, library, or codebase quickly: reading official documentation, building a small throwaway spike, and recognizing which concepts carry over from what you already know.

AI coding assistants are now part of this skill. Used well, they speed up exploration by explaining unfamiliar code, drafting tests, and suggesting approaches you hadn’t considered. Used badly, they produce code nobody on the team understands. The rule I’d follow: you’re responsible for every line you commit, whoever or whatever wrote it, so read it, test it, and be ready to defend it in review.

How to practice:

  • Learn your next library from its official docs and source instead of a video course, then write a short note for your team explaining it.
  • When an AI tool suggests code, ask it why that approach works, then check the answer against the documentation.
  • Build a small spike before committing to a new technology in production.

For a way to keep doing this alongside a full-time job, see how software engineers keep their skills current.

How to use this list without burning out

Don’t work on all ten at once. Pick two for the next three months: one technical skill your current work exposes as weak, and one non-technical skill your manager or teammates have mentioned. If you’re not sure, ask yourself which skill, if you got noticeably better at it, would most change how much your team relies on you. Start there.

Treat it like a small project: a goal, a weekly practice slot, and a check-in at the end of the quarter. To see how these skills map onto promotion levels, the junior to senior career roadmap lays out what changes at each stage.