Hi, thanks for watching, and welcome to coder++. The goal of this video is to help you become a better programmer. We’re going to do this through a series of 8 concise units. This will include looking at design principles used to create well architected applications; techniques for writing code with fewer bugs and better performance. As well as number of other topics related to improving your skills as a coder. Alright… let’s get started
Writing good software basically comes down to one thing: effectively managing complexity. Whilst our problems may be complex, it’s imperative that the code we write is as simple as possible. Simple code is not only easier to read, but it’s also much easier to change and maintain, not to mention far less likely to contain bugs.
There are a number of techniques we can use to accomplish this. At a high level it’s important that the software we create divides complex problems into simple more manageable ones. We can primarily accomplish this through separation of concerns.
In the book “Clean Code”, Robert C Martin uses a great metaphor where he compares good software to a city. While a single person couldn’t run a city, many departments and groups combined are able to accomplish it.
Well designed software should follow this approach. Different areas of the application should have their own responsibilities and between each of these areas there needs to be clean interfaces.
When deciding what to put where it’s important to design with high cohesion and loose coupling in mind. High cohesion means, that the functions contained in a class should all be closely related to helping the class perform it’s single responsibility. The ‘do one, and only one thing’ concept is important to keep in mind. Each piece of your application should have a single responsibility, and each responsibility of your application should be implemented in a single place.
Loose coupling means that as much as possible, the dependancies between your classes need to be minimized. One of the best ways to accomplish this is to write testable code. Tests can become an integral part of a maintainable application. They provide you with the confidence to make changes, while ensuring you didn’t break anything in the process. More importantly though, tests force you to think about your interfaces, because ultimately, code with numerous dependancies and poorly designed interfaces, is inherently difficult to test.
Just as our problems repeat so do their solutions. Design patterns are recurring solutions to common problems. The grand-daddy of all patterns books is ‘The Gang of Four Book’ And they stress two key techniques.
The first is to Favor object composition over class inheritance. When we learn about objects in school we’re led to believe that they’ll cure all that ails us. While encapsulation is an essential part of good code, the truth is that class inheritance creates incredibly tight coupling. Their second idea is to program to an interface, not to an implementation. Construct your applications out of reusable black boxes. It makes it easier to change up the implementation at run time, as well as help minimize dependencies.
It’s also important to build with the future in mind – you should be ready to extend. A feature may not be needed now , but it might be later. Try to make it easy as possible to implement the feature later on, minimizing rework as much as possible.
The key to clean code is always remembering that code needs to be written for people to read, not computers. Most of our time writing code is more accurately time spent reading it as we jump around trying to figure out what changes we need to make.
Let’s start by running through some do’s… The names you give your classes, functions and variables are what’s special about your code, the rest is just syntax and semi-colons.
For self documenting code let’s look at a code example. Inline documentation is often a bad idea. Over time the code changes and due to neglect the documentation starts to tell lies. The better way to document is by doing so in code.
As for white space Miles said it best: “It’s not the notes you play; it’s the notes you don’t play”. Space is incredibly important when it comes to readability.
Now, on to some don’ts…
Loops can sometimes end up with deep nesting. A good way to limit this is to use a continue. When expressing booleans it’s generally better to use the positive form, as it makes the conditions easier to follow.
There are no shortage of name choices: numPeople, totalPeople, countPeople, … it doesn’t matter which one you like, simply pick one and stick with it. It’s key that when you’re creating new variables there’s as little thought happening as possible. You want the decision to be obvious, and more importantly, the same every time.
One of the big arguments in code layout is where to put the curly bracket. On the same line saves space, while putting it on the following line enables you to more easily match up the brackets. These days most IDE’s have formatters which can automatically correct the formatting for you. These are great to share when working as a team.
Bugs are an inevitable part of the software development process. Your goal should be to create as few as possible rather than fix as many as possible. You’ll find that the more bugs you fix chances are the more that exist.
Bugs tend to cluster in the more complicated parts of the code. Simplifying these code paths will go a long way to reducing the total number. Additionally, each line of code you write is another place for a bug to hide. The less code you have, the less space there is for bugs.
One of the key techniques to reducing unnecessary code is making sure that there is as little code duplication as possible. Besides generating extra code it’s more likely that as requirements change the duplicate code will get out of sync.
Another key point related to bugs is the importance of fixing them as early as possible. Bugs found and fixed after an application has been released can be exponentially more expensive to fix than if they were fixed during the development cycle. Simply put, when developing, if you notice a bug, fix it. If you can’t fix it there and then, at the very least document the issue so you’re able to fix it as soon as you possibly can.
Lastly, it’s important to try to be as pessimistic as possible. Developers tend be optimistic people. While this is generally a good thing when it comes to considering ways for your application to fail, it’s helpful to assume the worst.
Expect web services you depend on to be unavailable and assume that hard drives will fail. Denis Waitley said it best… Expect the best, plan for the worst, and prepare to be surprised.
Performance comes down to bottlenecks. A chain is only as strong as it’s weakest link. It’s these key areas of the application which need to be optimized in order to speed up the application as a whole.
Although we write thousands of lines of code, very few of these lines are actually executed that often. The bulk of the execution time is handled by a small fraction of the code. Code coverage tools can be great at finding them.
When trying out changes it’s important to take measurements rather than just seeing if it feels faster. Sometimes a single change will make a small improvement, but a group of changes together can add up.
For many applications the bottleneck is the database. Although normalized tables are great sometimes a little de-normalization can go a long way in improving the performance. Star schemas are a good example of using a de-normalized data schema with the purpose of making reports faster.
Lastly, when we talk about performance it’s often more important to simply feel fast. Always show progress bars and if a request will take a while let the user know. When it comes down to it, it’s all about appearances.
Even if you’re only the only developer working on your application it’s important to follow some basic project management principles. The best way to create estimates is to break down your work into smaller tasks. The size of each task will depend on your experience level. The smaller the task you create the better your accuracy will be.
You’ll find that some tasks will take longer than expected while others will go more quickly. In the end they should more or less even themselves out and you’ll hopefully complete the project on time. At the very least if things are taking longer than expected you’ll know sooner rather than later that you’re falling behind schedule.
When estimating the time required for each of the tasks it’s important to factor in a buffer. You can analyze your past results to determine how much buffer to add. This should account for unforeseen problems.
The order of your tasks is also extremely important. Although it’s tempting to start with the more interesting challenges, it’s better to work on the riskier item first.
Finally, you can’t talk about software management without mentioning ‘The Mythical Man Month’. In a nutshell adding developers to a project will, initially at least, slow it down. New developers need training which will distract your experienced developers. Every developer added creates additional communication overhead. A small team of great programmers can outperform a large team of mediocre programmers any day.
They key to designing good user interfaces is making sure that your interface matches the user’s mental model. Steve Krug says it best in the title of his book, “don’t make me think”.
Users have been trained by every application they’ve used before yours, and they expect your application to work the same way. This is one area where innovation should be applied cautiously.
Good design is about making decisions on behalf of your users. You can’t simply make everything an option in the settings. As with your code, your graphical interfaces should be as simple as possible. While this may sound easy in principle, in practice this can sometimes be quite challenging.
Design should be an iterative process by which you continually refine the interface, stripping anyway the clutter until you’re left with the bare essentials.
Again, as with code, white space is critical. It divides up your interface into digestible sections. We’ve all seen websites where every space on the page is filled with something. The more you show your users sometimes the less they see. The same applies to text messages, less is more. Users simply don’t read. Keep your text as terse as possible and even then assume your users won’t read it.
A key goal in interface design is enabling your users to be as productive as possible. A great example of good vs bad design is the search feature in earlier version of Internet Explorer vs Firefox. In Internet Explorer, if a search failed, the user was blocked by a modal dialog box, and then needed to click a button to search again. In firefox the background just turns red.
While professional focus groups are pretty expensive, you can get most of the benefit by just calling in Rich from accounting. Make it fun for them, everyone likes to get a way from work for a bit. You’ll sometimes be shocked by what people try to do, it can be a great learning experience.
Lastly remember that the mouse can be hard to use even by dexterous people. One of the differences between Macs and Windows is that on the Mac the menubar of an application is always at the top of the screen. This give it essentially infinite height, throw your mouse to the top of the screen and you can’t miss it. Windows originally managed the complete opposite by having a single pixel gap below the start button, making it that much harder to click.
As coders, we spend the vast majority of our time at our desk working on our computers. It’s therefore makes a lot of sense to invest a bit of time making sure your environment is as efficient as possible.
First and foremost make sure you’re using a fast enough computer. Having to wait for your application to compile adds up over time. Additionally, if you can keep your compile time to under a couple of seconds, you’re less likely to browse reddit while you wait for your compilation to complete. If you like to tinker under the hood, installing a custom CPU cooler allows you to significantly overclock your CPU.
Size really does matter when it comes to screens. There have been numerous studies which have proven that larger screens significantly improve performance. Speak to your boss about getting an upgrade, it’s definitely in their best interest.
Consider turning off email alerts and checking your email every hour. Although responding to an email may only take you 15 seconds, context switching creates a much greater penalty. As coders we’re constantly juggling numerous classes, functions and variables in our head, essentially modeling the code in our minds.
Having to stop, even briefly, can create a serious delay when getting started again.
It’s worth making sure that your setup is as ergonomic as possible and that you’re using all ten fingers to type. From my own experience, I was blazing as a hunt and peck typist but ended up with a severe case of tendonitis. The solution for me was to learn to type using the Datahand keyboard. This involved a steep learning curve, but it was definitely worth it. Hopefully you won’t need to take such drastic measures, but, an ounce of prevention is worth a pound of cure.
As much as possible use keyboard shortcuts. Switching back and forth between the mouse and keyboard can be the source of a lot of wrist strain. It’s also just a lot quicker.
It goes without saying that you should use some form of version control. Git being a current favorite. In addition, it can be helpful to keep your code in a Dropbox folder so your changes are saved to the cloud automatically.
If your application relies on a remote service, taking the time at the beginning, to mock the data, always pays off in the long run. It enables you to work when you’re offline or when the service is down, and it makes it easier to test out different responses, as well as performing more quickly.
If you SSH into a number of different servers here are the steps required to use a shared key. This prevents you from having to type your password constantly thought the day. If you do this, ensure your computer itself is secure.
Make sure your deployment process is easy and reversible. Capistrano for Ruby on Rails is a great example of how good it can be. It makes deployment a one step process and it’s incredibly simple to roll back a change.
For task management the Getting Things Done or GTD approach is worth taking a look at. And for time management many people swear by the pomodoro technique. Whichever approaches you choose just make sure all pending tasks are written down. As much as possible you want to free your mind from having to worry about remembering things, and focus your energy on creating.
A great way to develop is to learn a new language. Each language provides it’s own benefits. When thinking about which language to learn next there are two great sites to check out. The one Google generally returns is Tiobe.com. It shows you the popularity of languages. Another great site to use is indeed.com. It gives you a sense of the marketplace for jobs.
Part of our growth comes from improving our coding skills but, it’s just as important to grow emotionally. We need to learn to accept changes and appreciate that sometimes, it’s only through having created the application, and then getting a chance to play with it, that we figure out how it needs to be changed for the better. You should try to look at each change as an opportunity to make the piece of code that you touch better than you found it. Constantly refactoring to improve the code. We learn from failure, not from success.
Although we get to spend vast quantities of time alone with our computer, there are times when we have to interact with other people. For what it’s a worth I’m a strong believer in the value of a having a drink with a coworker after work. Safely of course.
As for algorithms, you owe it to yourself to read ‘How to Win Friends & Influence People’. It breaks down human interaction into an exact science. Finally, if you haven’t done so already, start a blog. Even if you have nothing to say. Simply having the output can sometimes create the input.