Humans are complex creatures, with a wide variety of motivations for getting work done. Our motivation as developers is not purely extrinsic, or financial—sometimes it's for the joy of delivering code.
You might be a shipper, someone who enjoys shipping code quickly for the sense of achievement and being able to spend time with your family. This is the developer who calls Yagni in order to deliver something of value.
However, your colleague working from home two towns away might be a planner, someone who values the intrinsic reward of creating the right abstractions and really nailing that design.
The two of you might have a different how, but you both want the same outcome—productivity. Several of my former managers might disagree, but I believe that most people want to be productive, regardless of motivation. So, whether you are a shipper or a planner, here are six techniques that can help you maximize your productivity and speed up your delivery.
I once had a roommate who used my pliers as a hammer. He was able to successfully hang his pictures, but my pliers were damaged. A tack hammer would have done a faster job. Similarly, using the right coding language for your team's application can make a huge difference in getting the job done quickly and efficiently.
For example, Python has roots in scientific computing and is excellent for data science. Developers who value stability and consistent ways of approaching problems will love Python. In contrast, Ruby is excellent for expressive code for websites, and the Ruby community tolerates a plurality of approaches to the same problem.
PHP is an excellent option for fast server-side applications and can be deployed just about anywhere. There are legions of PHP developers available to work on your project, and the language and ecosystem have come a long way. Node.js allows web developers to use the same language on the server side as the client side, at the cost of some complexity. If you're looking for a highly interactive client-side experience, you might choose to optimize for Node.js.
I've worked on several projects where the team implemented custom-built authentication in the application, including saving salted password hashes to the database. We didn't need to. At the time, we had the infrastructure to delegate authentication to Active Directory so the users could enjoy the same password as their Windows login. However, Conway's Law and egos saw that possibility off: The development manager didn't like the infrastructure manager, and development happened where there was no access to the company network.
Now that Auth0 and similar products exist, do you really want to go through the trouble and risks of implementing authentication? It's arguably a better engineering trade-off to delegate authentication to Auth0 so you can get an MVP out the door, and then consider retrofitting for cost reasons as you scale. You'll also get many additional features and a better security footprint for your applications.
Lots of people build boats, but few build their own outboard motors. Why get stuck at sea when you can buy a Yamaha and get home?
We get it. Writing unit tests can be like brushing your teeth. Brushing teeth is better than tooth decay, however. Sometimes you just want to jump into implementing code, and tests get in the way. It's okay to feel that way.
Jumping straight into implementation without allowing tests to drive your design can feel productive, however, you can end up with code that smells—long methods and long parameter lists are just the start. In the end, your code will be harder to test and maintain. Sure, sometimes you need to explore an idea and smash out that code. Just be sure to do a
git reset and implement again with unit tests. You'll end up with a better design.
Bolted on, after-the-fact unit tests are very easy to spot. They are usually very coarse as they need to instantiate large classes and assert that they work. Such an approach doesn't give you the true value of unit tests. A well-written unit test will assist you in teasing out the concerns, and stubbing or mocking out dependencies that make things happen in the real world. Testing code that has real-world concerns can be difficult. It's much easier when you've done the work to separate the concerns.
If that's not enough to convince you, asking for more time so you can add tests _after _you've made the untested code work (and getting it past QA) is going to make your project manager angry. Don't even mention the word "Refactoring".
To be successful with unit tests, break the feature down into tiny cycles of "Red, Green, Refactor".
- Red—Start by writing a unit test that fails. Stub out the real-world activities so you can run unit tests any time you like.
- Green—Write the code that makes that unit test pass.
- Refactor—If you need to clean up your code, now is the time to refactor and run that unit test again.
Using this pattern, you'll gain more confidence in your code, and you can smile at the project manager as you tick the feature off as done.
When systems administrators wore army boots, frowned a lot, and put servers in racks, it was clear that their jobs were different than a developer's. Roles aren't as clear-cut today. Teams aren't staffed the way they used to be; we're expected have more skills than ever, and jump in wherever and whenever needed. Developers often spend too much time doing non-code tasks, such as infrastructure, DevOps, integrations, and so on. Yes, these tasks are important, but they don't make working software.
Infrastructure as a Service (IaaS) providers like AWS take the sheer effort of running infrastructure and make it disappear. You can deploy a new revision of your application with a simple git push. No need to perform subnet calculations or design network architectures, or argue with the DBA; the IaaS vendor takes care of that for you.
It gets better. There are hundreds of suppliers that take something that was once a project chore and offer their software as a service (SaaS).
For example, you don't need to configure Logstash and ElasticSearch replication. There are at least half a dozen companies that will ingest all the logs from your application, allow you to search them, and delete them after 90 days so you don't pick up a GDPR fine in the future. Many companies will also deliver email and SMS with nothing more than a credit card on file and an API client. Twenty years ago, those grumpy systems administrators would have been configuring Sendmail, reading logs, drinking coffee and cursing—all at the same time.
Of course, there's no silver bullet. You need to have a way to find the more reliable and scrupulous providers of all these services. The bazaar can be noisy. In addition, paying for all those vendors carries its own irritations. Ideally, you wouldn't put your manager's credit card in each app and have her do the monthly dance to get invoices for reimbursement.
And finally, Platform as a Service (PaaS) providers like Heroku have spent the last decade working out how to outsource not only application hosting, but also the add-on marketplace. They take care of the infrastructure _and _the platform with pre-configured installations of a wide variety of technology stacks. You can kick the tires on third party suppliers, get ideas to market in just hours, and easily pay for their services through one bill.
In Greek Mythology, Cassandra was a priestess of Apollo. Cassandra was cursed to have prophecies that were never to be believed. Your IDE is like Cassandra making prophecies—your cyclomatic complexity is too high, or that case block doesn't have a final default clause. If you don't listen to your IDE, you may spend a lot of time debugging.
In addition to the built-in features of your IDE or editor, there's a huge ecosystem of linters out there. SonarLint deserves a special mention as it supports most popular IDEs (Eclipse, IntelliJ, Visual Studio, VS Code) and provides top-notch recommendations for security issues, signs of subtle bugs, and code smells. That's a superpower if your team uses SonarQube for measuring code quality. Fixing the recommendations in your IDE means you'll be seen to commit secure and maintainable code.
Let your IDE be a _believable _prophet, like Athenais. The recommendations reduce risk and complexity in your code.
A good build tool is an invisible train that your code rides to production. As a solo developer you might choose to run your tests and deployment on a local dev environment from your IDE. That's great, as you will probably have a fast feedback loop. This allows you to experiment all day long with code.
However, as you begin to collaborate with others you'll want the code to run in CI/CD pipelines, which historically don't tend to play well with desktop dev tools. A Makefile, build.gradle, or other build tool that fits your language and runtime environment will slot in nicely and prove that your code and tests run clean outside your computer. Furthermore, you'll also gain a place to automate some deployment concerns, such as database migrations, packaging, distribution, and so on.
Furthermore, a well-designed build tool and implementation will reduce time spent on tasks that have already been done. You might want to run unit tests all the time, but is there any point in recompiling or linting code when none of the dependencies have changed?
Personally, I find a pain point in any new project where I've done some fundamental exploration in code and need to start down the path to production. Then, I switch my focus from writing code to exploring its interaction with the outside world. This is the time to implement your build process, then configure your IDE to run it.
Delivering functional software to users who appreciate it is very fulfilling. There are many factors that may be compromising your productivity and delivery time. However, regardless of whether you like to ship fast or slow, there are tangible steps you can take to make it easier and faster to deliver. I hope that one of the six productivity techniques that I have included in this article makes your job—and your life—a little easier. God speed.