We all want to write great code and feel like we’re contributing to the success of our team, but it takes more than just writing clean code or finding the perfect abstraction. Even as an individual contributor, there will be things you need to manage, such as your time and productivity. You’re directly responsible for making sure you’re using your time wisely and keeping your output high, but that’s easier said than done. Some days, you may feel like you’re getting a lot of work completed, while other days, you’ll feel completely stuck and not sure what to do next.
Delivering results is all about finding your personal groove that’ll allow you to churn through tasks and ship some actual code on a regular basis. That doesn’t mean you should lose sight of producing quality work, however. Your first focus should always be on quality code. If the code isn’t up to your team’s standards, then you should absolutely spend additional time cleaning it up so it’s ready for production. There’s no point in moving quickly if you’re shipping half-finished code that’s full of bugs—you’ll just be shifting the burden on to the rest of your team to find, fix, and maintain the defects in your work.
To be a productive software engineer, you should strive to continuously move forward and make progress toward building value and managing the risks involved in shipping code. So, let’s dive in and look at what you can do to increase your productivity and deliver better results.
Perfect Software Doesn’t Exist
The first point to remember is that perfect software doesn’t exist. There will always be multiple ways to solve a programming problem, and each comes with its own trade-offs. Some may be faster or scale more efficiently, while others may be easier to read and understand, maintain, or extend. Rarely will there be a perfect solution to a problem because there will always be compromises that come with each option.
There’s a model of constraints called the Project Management Triangle that states that the quality of work for a given project is bound by three things: the budget, the deadline, and the scope of a project. While it’s possible for the project manager to trade between these constraints, they’re only able to optimize for two of the three. Programmers and engineering managers commonly refer to this model when they state that software can be good, fast, and cheap, but the catch is that you can only choose two.
Fast and cheap projects tend to produce lower-quality software
Fast and high-quality software will be expensive to produce
High-quality and cheap software will be slow to produce
Figure: Project Management Triangle.
There will be times when you’ll be forced to make trade-offs or cut corners in order to meet a deadline or keep a project under budget. It happens all the time in software development. The reality is that you have to accept that some code you ship to production may be sloppy, inefficient, or could be optimized better if you had more time.
Junior engineers tend to keep tweaking and fine-tuning a solution because they want their code to be clean and perfect, while senior engineers tend to have a better feel for when a piece of code is “good enough” to ship, and then they move on to the next task. If you look closely when reviewing pull requests from your coworkers, you’ll begin to notice code written by senior engineers often has a hint of inelegance to it. They understand that it’s not worth the time to optimize and fine-tune each and every algorithm, abstraction, or variable name.
Unlock expert knowledge.
Learn in depth. Get instant, lifetime access to the entire book. Plus online resources and future updates.
Knowing when to ship “good enough” code is a skill that you’ll build over time, not something you can learn overnight. The more software you ship, the more you’ll understand where to focus your time and effort, and where you can write some quick and dirty code that gets the job done.
Find Tools That Work for You
If you ever talk to a great programmer, you’ll find he knows his tools like an artist knows his paintbrushes.Bill Gates*
As programmers, we have thousands of tools available that help us perform our jobs, and choosing the right tool for the job can increase your productivity tenfold. But a lot of powerful tools go underutilized because the programmer doesn’t understand how to use them to their full potential.
Before you can truly be productive as a programmer, you need to develop a deep understanding of the tools at your disposal. You should strive to regularly add new tools to your toolbox, but with each new tool you add, be sure to take the time to learn its advantages and disadvantages so you can know when (and when not) to reach for it. No tool is perfect for all situations, so don’t fall victim to relying on just one tool because you’re comfortable with it. Every tool has its strengths and weaknesses, and a good programmer knows when it’s best to use each one. These are the tools with which you will build great things, but you can’t do that until you know them inside and out.
As programmers, we commonly compare writing code to crafting software. Almost every line of code is handwritten for a specific problem you’re solving, so it feels natural to consider programming as a craft. There are quite a few similarities between programmers and other types of craftsmen like plumbers, electricians, and carpenters. Craftsmen are able to look at a problem, devise a solution, and use their tools to solve the problem. Sounds a lot like programming, right?
If you ever step into a carpenter’s workshop, you’ll immediately notice a myriad of tools they’ve amassed over the course of their career. You’ll find some general-purpose tools such as saws, routers, and drills that can be used for a variety of things. But you’ll also find very specific tools meant to do one thing and one thing very well, such as planers, clamps, custom-built jigs, and hand tools to gradually shape and refine the surface of a workpiece. Each tool in a carpenter’s workshop has a specific purpose to solve a specific problem, and an experienced carpenter knows exactly when each is the best tool to accomplish the task at hand.
Additionally, woodworking is extremely dangerous when you’re working with power tools and razor-sharp blades. To perform their job safely, an experienced carpenter also needs to know the limitations of their own skills and of each tool. If they use a tool incorrectly or are not paying attention, they could lose a limb or possibly their life.
While programming certainly isn’t as dangerous as carpentry or other professions, we too need to know the strengths, weaknesses, and limitations of our tools if we want to do our jobs efficiently and deliver quality software. Thanks to the explosion of open-source software available to us, choosing the right tool for the job is much harder today than it was ten or even twenty years ago. Not only are there thousands of programming languages, frameworks, and libraries to choose from now, their quality is continuously increasing as the industry learns how to build better software.
Modern software development, especially full-stack web development, requires programmers to switch between dozens of languages, frameworks, and libraries across all parts of the software stack. It’s nearly impossible to master all of the tools you work with on a day-to-day basis, so don’t feel pressure to learn every feature, syntax, and pattern. At a minimum though, you should at least have a good understanding of your tools and why you use one over another.
So, now let’s look at different types of tools and what you should know about them.
The integrated development environment (IDE) you choose for writing code can have a bigger impact on your productivity than you may realize. Modern IDEs are designed specifically to maximize developer productivity and are incredibly powerful. Your IDE should be considered one of the most important tools in your toolbox.
If you’re only using your IDE as a text editor to write and edit code, you’re likely missing out on powerful features you may not be aware of. Not using the full power of your IDE would be similar to buying an expensive chef’s knife and only using it to spread butter on your toast. That’s not what a chef’s knife is made for, and you wouldn’t be using the knife to its full potential. A good IDE used to its full potential can feel like it’s adding superpowers to your workflow and productivity because it can do a number of things that are difficult for humans.
exampleSo, how can an IDE make you more productive?
Save time by using shortcuts to switch between files quickly without leaving the keyboard.
Save time with shortcuts to open files from paths that were copied from stack traces, logs, documentation, etc.
Save time when needing to look up standard library functions with built-in API references.
Save time when refactoring by using smart find and replace features to search for and rename variables, class names, and file names in individual files or across your entire codebase.
Use code completion to reduce errors and prevent typos when typing variables and class names.
Use the power of debuggers to step through your code to track down bugs and performance issues in your code.
Catch syntax errors early with syntax highlighting; there’s no need to refresh the browser or wait for your code to compile.
Extend the IDE with plugins that offer new functionality, or build your own tools directly into the IDE.
If you haven’t already, I’d encourage you to try a few different IDEs to test drive them and see which one you like the most. Every programmer has their own unique workflow, so I’d recommend you stick with whatever IDE you feel most comfortable working with. After all, you’ll be spending a significant part of your career writing code in an IDE, so it’s worth it to spend the time to find one that works for you and learn how to use it to its full potential.
exampleSome IDEs to consider trying out:
Android Studio. An IDE created by JetBrains that is geared specifically toward Android development.
Visual Studio Code. Created by Microsoft, this has become one of the most popular IDEs used by developers due to its large ecosystem of extensions to add additional functionality and customization.
XCode. Created by Apple, this IDE combines tools to design, code, test, and debug iOS and MacOS applications in one application.
The Command Line
Integrated development environments typically include GUIs, which make them user-friendly and easy to get started with. We’re all familiar with browsers and desktop applications, so a GUI-based code editor seems like a natural first step when it comes to writing and editing code. But there are a lot of things that you cannot do with GUI applications.
Command-line terminals, on the other hand, often seem confusing and intimidating to those who are unfamiliar with them. You’ll probably have some difficulties when you’re first learning how to navigate and run commands on the command line, but over time, these actions will become second nature and easier to remember.
The command-line terminal will become an essential tool on your road to becoming a senior engineer. It’s probably the most powerful tool in your toolbox, even more so than your IDE, so it’s a good idea to get familiar with it as soon as you can. The command line can amplify your skills and increase your productivity by orders of magnitude, but you need to know how to use it to its full capabilities before you can unlock that potential.
exampleHere are some examples of how the command line can make you more productive:
Save time by creating, copying, renaming, and deleting files without leaving the keyboard.
Quickly search for phrases and patterns across your entire codebase or file system.
Chain commands together to pipe output from one program to another, allowing you to create powerful pipelines to process data.
Create your own shortcuts and aliases to speed up repetitive tasks and commonly used commands.
Create your own utilities to extend the power of the command line.
Write and execute scripts to automate larger tasks.
Install dependencies, frameworks, libraries, and other tools with a few commands.
Monitor and analyze current running processes and system diagnostic information on your host machine.
Access remote servers and perform network operations to monitor and analyze network traffic.
There are endless things you can do with the command line—you’re only limited by your imagination. The more you use it and become comfortable with never leaving the keyboard, the more creative you’ll get when it comes to thinking up new ways to solve problems directly from the command line.
Don’t worry about speed when you’re first starting out. We can’t all be as smooth as hackers in the movies are, and the good thing is we don’t need to be. Just focus on learning the command line at your own pace. The speed will come with repetition and practice. The more important thing to focus on is getting comfortable and learning how to solve the problem you’re working on with the commands you have at your disposal. Everything else will come naturally, and you’ll be boosting your productivity in no time.
It goes without saying that if you’re hoping to make a career as a professional programmer, you should strive to have an expert-level knowledge of at least one programming language.
It might sound silly because it’s so obvious, but there are a lot of developers who jump from language to language because they’re chasing the hot new trendy tool that everyone’s talking about. If you’re serious about having a long and successful career as a software engineer, you need discipline and focus to stick with one language long enough to become an expert in it.
Ideally, the language you will pick will pay the bills by enabling you to leverage your expertise into lucrative employment opportunities. Spending the time to become an expert in one language pays off because it goes a long way toward providing job security.
Whatever language you choose early in your career, stick with it until you become an expert in it. Learn the language’s strengths—what problems is it really good at solving? What are its weaknesses? It’s also important to learn its inconsistencies and any pain points so you know when to avoid them.
Only after you feel comfortable writing code at an advanced level in one language should you begin to branch out and learn other languages. While you’ll want to stick with one or two languages in the first few years of your career, learning how to write programs in different languages is an important skill to learn as you gain more experience. You’ll learn new patterns, techniques, and even new programming paradigms by learning new languages. It will force you to become a better programmer as you apply what you’ve learned to the code you write, even if it’s in a completely different language. Solving problems in different programming languages forces you to rely on your knowledge of the foundational concepts of programming rather than the syntax of one language over another.
At some point, you’ll work on a new project and have the opportunity to decide which language to choose. If you have the knowledge about the strengths and weaknesses of different languages, you’ll be able to make better decisions on which to choose. And in some cases, choosing the right language can increase your team’s productivity by an order of magnitude.
Knowledge of the right tools at the right times can unlock amazing opportunities for you and your career.
Some tools and technologies are so common across the software industry that all software engineers should at least have a general understanding of them. Familiarity with these technologies make you a well-rounded engineer.
Tools that every software engineer should be familiar with:
This is just a small list of common tools that are universally used by software developers at some point in their career. You don’t need to become an expert in each tool, but having at least a general understanding of each of them will round out your technical skills.
Once you’ve mastered your tools, the next thing to focus on is your own process for producing software. Every programmer approaches software development differently, and what works for some people may not work for others. There are several development methodologies and ways to solve problems, but I’m going to share a process with you that I’ve found works for a lot of programmers. It’s simple and straightforward, and it helps you stay focused on the important thing—delivering working software. So, here’s the process:
Make it work, make it right, make it fast.
That’s a quote from Kent Beck, the creator of extreme programming and one of the original signatories of the Agile Manifesto. Kent has shaped programming in many ways, and this technique will hopefully shape the way you approach programming. Following this simple pattern will help you manage the complexity of your own solutions and prevent you from trying to do too much all at once.
Let’s dive into it a little more.
Make It Work
When you first set out to write new code for a feature or bug fix, you should be fully focused on proving the problem can be solved. Without this step, nothing else matters, so you should always value a working solution, even if it’s messy, over something clever that doesn’t compile.
important You are allowed to violate principles of good software design in this phase because your only goal is to make things work and work repeatedly.
This phase of software development can be compared to writing the first draft of an essay, so it’s okay to have ugly and messy code while you’re working towards a solution. You don’t need to come up with good variable names or a good object-oriented design, and your code doesn’t need to be elegant. It’s completely fine to start out scripting a solution at first, if that makes it easier for you to focus on solving the problem.
Once you are able to compile your code and run it, and you have somewhat of a working solution to the problem you’re solving, save your progress. Commit your changes to version control. Doing this will give you a good stopping point where you know you have a working solution. Now that you have at least something working, you can begin to refactor and improve upon the solution.
Make It Right
Once you have a solution that you’ve proven works repeatedly, it’s time to move on to the next phase, which is to make it right.
To follow the essay comparison, this would be the revision phase. Here you should focus on improving the design, reliability, and readability of your code. This is where you refactor the code you just wrote—clean up your naming conventions, introduce abstractions and interfaces to aid in extensibility, and add tests. Make sure to cover all your edge cases and remove any hard-coded values you may have used in the first phase.
Your goal is to clean up the code so that when you return to it in the future you’ll be able to understand what it’s doing and easily change it if needed. There may be other programmers on your team that could be extending it in the future, so keep that audience in mind when refactoring and documenting.
Your code should be bulletproof at the end of this phase. You should be confident in your solution and comfortable shipping this code to production.
Make It Fast
The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.Donald Knuth, The Art of Computer Programming
This last step, which happens to be the hardest, is to improve the performance of your solution by making it fast. The reason it’s the hardest step isn’t necessarily because performance gains can be hard to come by, but because it can often be hard to find the time or resources in order to properly improve the performance of your solution.
After you’ve finished the “make it right” step, your code should technically be ready for production. You should be able to ship it and move on to the next feature or bug fix that provides value for your customers. And this is what makes the “make it fast” step so difficult, because you may be able to provide more overall value by ignoring this step completely and working on a new ticket.
In some cases, shaving a few milliseconds off the run time for your solution may not be the best use of your time, because that’s not what’s adding the most value for your customer. It’s a good idea to communicate with your manager before starting this step, because they may have other projects that take priority over optimizing your solution.
In the end, your goal is to provide software solutions that provide value to your customers, so it’s important to be aware of how much time you’re spending on performance optimizations. Most of the time, “good enough” will be fine to satisfy the requirements so you can move on to the next project coming down the pipeline.
Solve the Problem First
In the earlier section about managing risk, we explored the importance of planning ahead when working on large projects. That idea also applies to smaller, individual tasks you work on as well. When you pull a new ticket to work on, what you should not do is start coding right away, even if you think you may know how to solve the problem. It’s a trap a lot of developers fall into, and it’s potentially a risk because you could be wasting your time and effort implementing the wrong solution.
If you don’t have a good understanding of the problem, the requirements, and the acceptance criteria, you run the risk of shipping code that doesn’t actually solve the issue or that may not be the optimal solution.
So, what should you do when you start a new task?
Read the task description and take note of any requirements. It’s sometimes helpful to make a checklist of requirements so you can be sure you’ve checked them all off before submitting your code for review.
Read the task description again to make sure you fully understand the problem and what needs to be done. Sometimes it takes a few passes to fully understand what is being requested in the ticket.
Write down any questions you might have about any of the requirements in the ticket. It helps to get your thoughts out of your head and written down in case you get distracted or need to temporarily shift your focus to another task.
Browse through the codebase to get familiar with the code you think you’ll need to change. There may be technical debt you’ll need to work around, or there may be some abstractions that your solution may need to fit into, so it’s a good idea to poke around the codebase to make sure you know what you’re working with.
Come up with an implementation plan. By now, you should have a good understanding of the problem and any technical limitations you may need to work around.
Once you’ve worked through all the steps above, the actual coding part should hopefully go smoothly. If you’ve done good research and planned out what changes need to happen before writing any code, it should be fairly straightforward to implement your plan. Most of the work was already done ahead of time, so you just need to translate that plan to code a computer can understand.
By doing all the heavy lifting during the planning phase, you’re reducing the probability that you’ll need to change direction and rewrite code. Don’t assume that code is easy to change and that you can just delete it and write it again. Changing direction after you’ve begun implementing a solution costs money, especially when you’re dealing with large codebases with millions of lines of code.
important It is faster and cheaper to refactor an idea than it is to refactor code.
Solving the problem first allows you to move faster because you’ve already identified how your solution will fit into the existing code, and you already have an idea of what code needs to be refactored, improved, or moved around to implement your solution. Plus, you can present your solution to your teammates to gather feedback before writing any code. It’s possible they may come back with questions or concerns, or they may suggest a better approach that is more efficient or simpler than your original idea. (Occasionally, on complex and important problems, an engineer might both design and prototype multiple solutions—for example, to then compare their performance. But this is a rare situation.)
In order to move quickly and deliver results consistently, you need to learn how to think strategically. Spending time upfront planning your implementation before coding it will reduce costs and time later when it comes to coding your solution.
If you’re working towards a promotion to a senior role, your manager may tell you that they’d like you to “take more ownership” of certain projects or tasks that your team is responsible for. To many people, “taking ownership” sounds vague and ambiguous the first time they hear it. Take ownership of what, exactly?
There are a lot of differing opinions on what taking ownership means, so if your manager encourages you to do so, the best thing to do is to simply ask them what taking ownership means to them. It’s always good to clarify their expectations to make sure you don’t miss something they are expecting you to do.
A developer taking ownership of something commonly means they are taking on more accountability for the success of a certain project or task. They are not necessarily the only one responsible for the success or failure of a project, but they will have a bigger influence on the outcome.
exampleA junior engineer will typically work on tasks as part of a larger project. The requirements for those tasks were probably defined by someone more senior than them, either a senior engineer or a manager, and the junior engineer is just implementing a solution to meet those requirements.
As that junior engineer takes on more ownership, they’ll start to get more involved in the planning aspects of the projects rather than just implementing someone else’s plan.
That junior engineer will now add more tickets to the backlog and make sure all the project requirements are defined so that they can be sure the project is meeting the customer’s needs. They’ll help research, plan, and prioritize in case there are any internal or external dependencies that dictate when things need to be done. And they’ll coordinate the release of the code to production, measure its success, and contribute to the ongoing maintenance of the project they led from start to finish.
That junior engineer took on more responsibility for the outcome of the project.
The path to success for a project is never as simple as it looks, and the more ownership you take on as you work towards a promotion, the more ambiguity you’ll be dealing with, which can be difficult. Not only do you need to chip away at tasks to get to the finish line, but you also need to look at the end goals of the project and work backwards to figure out a plan for how to reach the finish line. You may not even know where to begin at times, but that’s part of taking ownership and growing into a senior role. Taking ownership means figuring out a path forward, even when faced with uncertainty about how to proceed.
Ownership isn’t always directly tied to specific projects, either. Sometimes taking ownership of areas involved in a team’s development process is a good way to show maturity. When a developer’s mindset shifts from advancing their own technical abilities to advancing those of their team, they begin the process of thinking like a senior engineer.
While their main projects and priorities always come first, senior developers don’t sit back and wait to be told what to work on. They actively seek out areas where they can improve the codebase or, even better, improve their team’s development process.
Senior engineers learn to put their team’s needs above their own. That often means that senior engineers work on things such as:
Helping unblock other developers if they are stuck or if they are waiting on a code review.
Working on the mundane but important tasks, such as bug fixes or writing documentation.
Helping to teach other engineers and transfer knowledge across the team.
In essence, taking ownership is all about taking on more responsibility for the outcome and results of individual projects and for the results for your team. As you work towards a senior title, try to identify areas where you can contribute more on individual projects, and try to take on more responsibility in areas where you think you can improve your team’s overall output. The more results you can help deliver, whether through your own work or enabling others, the higher the chances of earning that promotion to a senior role.
It’s a popular stereotype that programmers are often introverted, reclusive, and lacking in the social skills department. Even though this doesn’t accurately reflect the industry as a whole, it’s been perpetuated in anecdotal stories of old-school hackers in the early days of Microsoft, Apple, Facebook, and many more tech giants. There’s a common theme in Hollywood movies in which the genius coder hacks into the mainframe from his dark basement, surrounded by empty pizza boxes and energy drinks. But while the act of programming has always been between a human and a machine, the software products used by people throughout the world are a result of collaboration and communication between many different people. Shipping software at scale is a team effort, and it takes good communication skills to deliver quality products.
Over the years, we’ve gotten better at building software. We’ve learned from our mistakes; invented new algorithms, software patterns, user interfaces, and development methodologies; and built languages and tools that have increased the speed and quality with which we can produce software. As the industry changes, so has the role of programmers. It’s no longer enough to be technically competent—communication skills are just as important as technical ability and will continue to play a critical role in software production for years to come.
Although the act of programming is mostly an individual one, working on a team with other technical and nontechnical people will be one of the hardest things you do in your career. Human nature is incredibly complex and unpredictable at times, and as programmers, we’re required to interact with many people throughout the software development process in order to perform our jobs.
You’re reading a preview of an online book. Buy it now for lifetime access to expert knowledge, including future updates.