Christian Maioli M.

đź›  Web development and đź–Ą computer programming

Creative techniques for writing modular code

Modularity first started looking really interesting to me after I read the legendary SICP book. It surprised me to learn that for some people, making code understandable is so important that they write their own little languages just to make the program readable to anyone (the book also teaches you how to build an interpreter, an encoder, and other cool things).

Although SICP gives you a solid introduction to the art of modularity, it’s still hard to create your own vocabulary if you’re not using Lisp. For other languages, we have to get creative to be able to encapsulate code into bite-sized chunks.

Why and how to write modular code

Modularity exists entirely because of the human side of development. A computer doesn’t need a broken-down and embellished version of the code to be able to run it. It was our cognitive limitations that got us to write code in smaller pieces at a time.

A particular problem where this becomes obvious is when dealing with temporary contexts. For example, say you’re writing to a file but a condition arises where you need to write to another file. You need to temporarily forget about the first file and all of its related data to deal with the second one instead.

This kind of situation was confusing and overwhelming, so functions were invented. A typical way to handle this now is by making a function that writes arbitrary data to any file, to isolate the temporary context inside it.

Using variables to isolate logical expressions 🎲

What happens if the context is actually very small? Even a function might be overkill. Another way to handle temporary context is by putting logic inside variables. Taking advantage of the fact that logical expressions get reduced to a boolean value, most languages will let you do this.

We know that these small modularization ideas are beneficial when we can appreciate that code starts to read like a story, more than a group of disconnected formulas. When you make changes to your code, ask yourself: does this change make the implementation’s logic more evident or does it make it inscrutable?

Using nested functions đź“š to encapsulate blocks of code

Sometimes when you’re writing a function you realize that some of the code looks repetitive, so you decide to create another function. Most languages would require you to make the new function under the same namespace. This is where JS nested functions come in handy. You can place a function inside another one, so that the internal one can only be accessed from your original method.

An interesting property of this technique comes up when you use IDEs that have code folding. They allow you to fold the external and internal functions together in this case, which is much faster than folding individual ones.

Preventing data access đź”’ with block-scoped variables

Some languages such as C, C++ and more recently JavaScript (ES6) let you define variables that are only accessible within a block. That means that you can’t accidentally access that variable later, or in another iteration of the same loop.

These come in handy when you want to encapsulate data access to the smallest part of the code that you can. Any change you make to your code is bound to make ripples through your program. If you modify a lot of code, there will likely be a strong impact on the codebase. For a larger codebase, even a small change could have a large impact.

That’s why these features let you avoid bugs resulting from accidental data modifications. Block-scoped variables localize the impact of changes.

Although block scopes can let you reuse variable names, that’s not what they are most useful for. The real benefit comes from not being able to access the internal data. You’re creating a safety net so that when a bug comes up, you know that you can ignore internal variables if the problem is outside the block.

Why should I apply this stuff?

Identifying new ways to separate concerns in your code is important, because it lets your mind worry about less things when debugging. You should aim to keep a high cohesion and encapsulation, and having as many tools as possible to do that will make you more productive. Once you are used to focusing on making your code tight, you will have less bugs to worry about.

Cohesion and coupling are really important concepts for writing clean code. Fortunately, there are already many good articles about them. Here’s a great introduction to cohesion. If you’re not familiar with these ideas, the article is somewhat technical, but definitely worth studying.

Modularity sometimes causes more problems than it solves 🛑

After you begin to appreciate the value of breaking ideas down into smaller pieces, there’s a risk that you’ll see it as the silver bullet of clean code. You wouldn’t be the first.

Modularity is no panacea. Let me show you some examples where it causes more problems than it solves.

Excessive object-oriented structure

Programming using interfaces (contracts) is a very powerful idea. Taking advantage of that, some frameworks bring with them a whole set of interchangeable classes. For example, to handle persistence there might be several classes implementing the persistence interface. Unfortunately, IDEs get confused by this, so while reading code you will try to find the source of a method, and the IDE will not know which method you want to see, so it will show you a long list of files where a method with that name exists. It can be a real pain to have to sift through that list every time.

Modules that are too small

There are tons of JS modules out there that include just a single small function. Each module requires additional parsing and processing time, and includes their own header in the code, so using a lot of small modules will add overhead to your build system and increase your bundle size.

Centralization of dependencies

Excessive modularity has also caused problems due to the centralization of code, which happens once enough people start using a module. That was the case with left-pad, which was brought down, taking many projects with it for a few days.

Refactoring for the sake of it

Some code almost never changes. In those cases it might not make sense to try to make it look cleaner or to abstract logic that is only used there and that already works well. The other day I was reading the Redis code and a method stood out, “loadServerConfigFromString”. I thought, this code doesn’t look too pretty and yet, according to git blame, it has not changed much in the last 7 years. There is no reason to refactor code that never changes and already works fine.

Conclusion

One of my core philosophies when working with technical issues is always being aware that the implementation details alone will make or break an idea. Always be aware of where and how you are going to apply modularity, and how it affects the development environment.

Being Agile and working smart are not the same thing

The discussion about software methodologies is not over. Agile did not win, nor did any other methodology. I’ve never worked in a team that hit the right spot in team dynamics and was as effective as possible, whether using incremental development or not. I think I’ve figured out what it is that still needs work in that area.

Most discussion about methodologies is about trying to find an ideal one. Should it be more structured? Less structured? How many meetings are we doing? You try a bunch of recipes such as Scrum, Kanban, waterfall; sometimes they work, and sometimes they don’t. It’s like aiming at a moving target.

The problem is not which recipe to use, it’s being aware of each one’s strengths and weaknesses. Teams need to know when structure is beneficial and when it’s not—and realize that achieving good team dynamics is a skill to be honed. You can’t write down a process plan and expect everything to magically be smooth sailing from then on, because project requirements vary in many ways that sometimes aren’t self-evident.

Even throughout a single project, using a single methodology is far from ideal. For example, are short iterations a good fit for a project that needs big design up front, no matter what? And if you use long iterations, what happens later when you realize your design is lacking in a specific area?

Looking over these issues, I’m going to make a case for why:

  • Being Agile and working smart are not always the same thing
  • An adaptive mindset means knowing when your methodology helps/hinders you
  • Office life and daily issues can completely change the effectiveness of your process

How development focus changes throughout a project

In a large project, the teams focus is constantly changing. When development begins, you might split the project into modules, with strong code ownership rules and laser-focused development.

Later on, when most modules are going through a debugging and testing phase, code ownership gets in the way. At that stage it’s more productive for a developer to go through his own short testing/debugging cycle and be able to fix bugs anywhere, and excessive communication would slow her down considerably. Although someone with more knowledge about a module will be more productive when working on that part of the code, on the other hand, more eyes on the same piece of code will lead to less defects.

Perhaps the realization that things change constantly is what brought us to have more granular methodologies like RUP, with it’s bazillion roles and artifacts. A highly dynamic methodology like RUP requires people to learn new habits constantly. It’s hard to even get people to make a stand-up last 15 minutes. I don’t want to know what happens when you make them jump through 10 times as many hoops.

More structure means more concepts to learn, a higher cognitive load, and a harder to apply methodology. If the question is how to make the work flow as smoothly as possible, then jumping through more hoops is not the answer.

Is it right to use Agile everywhere?

Agile is lightweight, easy to implement, and the project’s direction can be adjusted based on completed work, not on speculation or predictions. It can adapt better to changes, but sometimes it prevents you from going where your project needs you to go. Here are a few cases where it’s iterative process doesn’t work so well:

  • You need a big design phase up front. You need precision and don’t care so much about development time. Anything NASA does would fit here.
  • Your business has a high cognitive load. It’s hard for users and/or developers to get it, so you need a lot of well-organized documentation. Enterprise software. Financial software.
  • You’re doing a complete rewrite of your software, so your team already has a clear idea of where to go. Requirements are clear, and there’s not much need for course-correction anymore.

There is a lot of focus right now on getting the kinks of Agile just right, and it’s a productive mission because most teams don’t get it right. On the other hand, to realise the full potential of your team you need to know when to switch things up, and when Agile is setting you back.

Besides those specific cases, it’s a good idea to always keep in mind the disadvantages of Agile:

  • It requires high customer commitment. Some customers are too busy for that.
  • Requires constant and/or automated testing. Some customers don’t want to spend money on that.
  • Huge communication overhead for remote teams. Prepare to spend more time setting up WebEx/Skype and on IT issues than actually writing code.

These problems are responsible for the fact that most companies today are applying some kind of hybrid Agile process rather than staying true to it’s principles. But it’s important to know why Agile exists, what problems it solves, and when to stick to the core Agile principles. This is why I said that achieving good team dynamics is a skill.

When to make changes to your team

Typically a team stays mostly the same throughout a project, but as the project progresses, it’s unlikely that everyone’s strengths are being leveraged at all times. People are good at different things.

Generally speaking, more experienced team members are better leveraged by having them use their experience more than their mechanical skills. The inverse is true for the least experienced.

Put both kinds of developers to solve a restricted coding challenge and the more experienced one might do it slightly faster, but he knows he can entirely avoid writing any code by just calling a specific library that a less experienced developer won’t even know exists. That difference in experience may well be where the “10x programmer” expression comes from.

One of the main focuses in modern software development has been in reducing cycle times, but they are an incomplete measure of team productivity. Getting stuff out faster will let you adjust more often, but that won’t help you if you’re not willing to use the right people for each problem, and taking their specific experiences into account. Familiarity with a problem is a huge leverage point, and modern methodologies throw it under the rug when they turn the whole team into numbers, each one with identical weight.

Adapt your daily routines to your current team

The final aspect you have to look at when you want to be a fully adaptive team is the details of your daily routines. These can make or break your methodology’s effectiveness.

I remember being in an Agile team where the daily standups were contributing almost nothing due to several factors. You might have seen it yourself, when noone has anything to report in the standup for days in a row. The thing that still made standups effective in that case was the fact that my desk was literally 2 meters away from where we would meet every morning. The effort to get there was so small that it didn’t matter.

A poor implementation of a methodology can still work if the specifics are adjusted to make up for the issues.

I’ve also seen the opposite situation, where you have a well applied methodology that is made worthless by the accumulation of small daily issues, like video conferencing software that never works and poor hardware or infrastructure.

The bottom line is that to make your development process work, you have to take a hard look at who you’re working with, how they like to get things done, and what resources you have at your disposal, and then make every effort you can to help your team stay in the zone. Make the working environment comfortable and effortless and the work will flow.

If your team has lots of experience, trust them, and help them enjoy getting things done. Don’t undermine the relationship by requiring them to jump through hoops to get work done.

Note: this post was originally featured on TechBeacon.

Avoid these 35 habits that lead to unmaintainable code

Bad habits are hard to break and even harder if you don’t realize that what you’re doing is undermining your work. If you know but don’t care—that would be the worst. But you’re here, aren’t you?

As a programmer, I’ve seen a lot of poor practices, not just around code, but also around teamwork skills. I’ve been guilty of practicing many of these bad habits myself. Here are my top 35 bad programming habits, organized into four categories: code organization, teamwork, writing code, and testing and maintenance.

Code organization

1. Saying “I’ll fix it later”, and never doing it

The habit of postponing code fixes is not merely a problem of priorities. Organizing your issue tracker might generate some progress, but you also need to have a way of tracking smaller issues that come up. Adding “TODO” comments is a quick way of making sure you don’t miss anything.

2. Insisting on a one-liner solution

Being obsessive about writing efficient, elegant pieces of code is a common trait of programmers. It’s like solving a puzzle—you find a combination of functions and regular expressions that turn 20 code lines into 2 or 3. Unfortunately, it doesn’t always result in readable code, and that’s generally the far more important outcome. Make your code accessible first, then clever.

3. Making pointless optimizations

Another place where we often misplace our efforts is optimizations. It sounds great to reduce the size of your website a few bytes, but won’t gzip make up for it anyway? And aren’t requests more important? Address optimizations at the end of a project, because more often than not, requirements will change, and your time will have been wasted.

“Premature optimization is the root of all evil.”
—Donald Knuth

4. Convincing yourself that styling issues are not that important

If I’ve learned anything over years of looking at other people’s code, it’s that dealing with coding style issues is the thing that developers are most likely to postpone. Maybe it’s hard for inexperienced coders to see what good will come out of addressing styling issues, but over time it will become evident that once code quality derails, a snowball effect will turn any project into a complete mess. Be strict about best practices even if they seem negligible. Set up code checking and linting tools to give yourself space to worry about the more important things.

5. Sweeping things under the rug

Either by catching and ignoring exceptions, or by using libraries that don’t report errors (such as jQuery), there are many ways to sweep things under the rug. But when one of those errors becomes a priority, the challenge of fixing it will be many times greater, considering that you won’t have a clue where to begin. An easy way to avert this is by logging those ignored errors so you can study them later.

6. Using names that don’t add information

Naming is hard, but there’s an easy way to make sure your variable and function names are at least of decent quality. So long as the names add some kind of information that the rest of the code doesn’t convey, other developers will have an easier time reading your code. The reason that naming is so important is that names can give a general idea of what the code does. It takes more time if you need to dig into the calculations to figure out what piece of code does, but a good name can help you understand what the code does in seconds.

7. Ignoring proven best practices

Code reviews, test-driven development, quality assurance, deployment automation—these practices, and several others, have proved their value in countless projects, which is why developers blog about them constantly. A great reference for these best practices is the book Making Software: What Really Works, and Why We Believe It. Take the time to learn how to do them properly, and your development process will improve in all of your projects in ways that will surprise you.

Teamwork

8. Abandoning plans too early

A sure-fire way for making your system inscrutable is to not commit to a plan. You can always say, whenever your code is criticized, that the plan isn’t complete. However, having half-done modules will lead to tightly coupled code as soon as you try to make those unfinished modules work with each other. This kind of complication also comes up when a project’s leadership roles change and the new leads decide that having it their way is more important than architectural consistency.

9. Insisting on a plan that has little chance of working

Just as abandoning your plans can cause problems, so can sticking to a plan that doesn’t work. That’s why you should share your ideas with your team to get feedback and advice when things get tricky. Sometimes a different perspective can make all the difference.

10. Working on your own all the time

You should strive to share your progress and ideas with the team. Sometimes you think you’re building something the right way, but you’re not, so constant communication is very valuable. It’s also beneficial for other people when you work with them. Their work often improves when you discuss ideas with them and mentor the less experienced members of your team, who are more likely to get stuck.

11. Refusing to write bad code

There comes a time in every developer’s life when deadlines will force you to write terrible code, and that’s okay. You’ve tried warning your client or manager about the consequences, but they insist on sticking to the deadline, so now it’s time to code. Or perhaps there’s an urgent bug that can’t wait for you to come up with a clean solution. That’s why it’s important to be versatile as a programmer and to be able to write poor code very quickly as well as good code. Hopefully, you can revisit the code and pay back the technical debt.

12. Blaming others

It’s no secret that arrogance is an all-too-common trait among developers and other technical professionals. Taking responsibility for your mistakes is a virtue that will make you shine among your peers. Don’t be afraid to admit that you’ve made a mistake. Once you’re okay with that, you will be free to focus on learning why you made that mistake and how to avoid it. If you don’t own up to it, learning becomes impossible.

13. Not sharing with your team what you’ve learned

Your value as a developer is not only placed on the code you write, but also on what you learn when writing it. Share your experiences, write comments about it, let others know why things are the way they are, and help them learn new things about the project and its intricacies.

14. Being too slow on giving feedback to managers/clients

One of the most valuable character traits of any craftsman lies in making sure that everyone is on the same page about the work, as much as possible. The reason for this is not so that your manager call fill spreadsheets. It’s for your own gain as well: You will have fewer insecurities and reduce uncertainty about the lifetime and future of the project.

15. Not using Google enough

The best way of solving a complex problem quickly is not having to solve it at all. When in doubt, Google it. Of course, you can bother the engineer next to you instead, but rarely will he be able to give a response as detailed as Stack Overflow, not to mention that you’ll be interrupting his work as well.

16. Overvaluing your personal style

Always aim to coordinate your working style and environment setup with your team. Ideally, everyone on your team should be working under similar conditions and following the same coding style. Doing things your way can be more fun, but co-workers might not be used to your coding style, and if it’s unusual, it will be harder for the next developer to work on what you’ve built.

17. Having a personal attachment to your code

When someone comments on your code, don’t take it personally. Your code should stand on solid ground; that is, you should be able to explain why you wrote it that way. If it needs improvement, that’s only a reflection of the code’s correctness, not of yourself.

Writing code

18. Not knowing how to optimize

A good optimization strategy takes some experience to get right. It takes exploration, analysis, and knowing every system involved in a process. Inform yourself about these things. Learn about algorithmic complexity, database query evaluation, protocols, and how to measure performance in general.

19. Using the wrong tool for the job

You can only know so much, but the reason why you have to keep learning is that each new problem brings a different context and requires a different tool—one more applicable to the task at hand. Be open to new libraries and languages. Don’t make decisions based strictly on what you know.

20. Not bothering with mastering your tools and IDE

Each new hotkey, shortcut, or parameter you learn while using the tools you work with every day will have a more positive effect on your coding speed than you realize. It’s not about saving a few seconds by using a hotkey; it’s about reducing the context switching. The more time you spend on each small action, the less time you’ll have available to think about why you’re doing it and about what comes next. Mastering shortcuts will free your mind.

21. Ignoring error messages

Don’t assume that you know what’s wrong with your code without even reading an error message, or that you’ll figure it out quickly enough. Having more information about a problem is always better, and taking the time to gather that information will save more time in the long run.

22. Romanticizing your developer toolkit

Sometimes your preferred editor or command line tool isn’t the the best tool for the job at hand. Visual Studio is great for writing IDEs, Sublime is great for dynamic languages, Eclipse is great for Java, and so on. You might love vim or emacs, but that doesn’t mean that it’s the right tool for every job.

23. Hardcoding values instead of making them configurable

Always be thinking about what changes might come and how to deal with them. Technical debt will grow at a monstrous rate if you don’t separate the moving pieces from the rest of your work. Use constants and configuration files where appropriate.

24. Reinventing the wheel all the time

Don’t write code you don’t need to. Perhaps someone else has spent a good deal of time on your problem already, and he or she might have a well-tested solution that you can reuse. Save yourself some trouble.

25. Blindly copy/pasting code

Understand code before you reuse it. Sometimes you don’t immediately notice everything the code is doing on first glance. You will also learn more about a problem when you take the time to read the code in detail.

26. Not taking the time to learn how things really work

Always take the opportunity to expand your knowledge by thinking about how things work and reading about their underlying issues. You might save time by not bothering right now, but what you learn on a project will be more important in the long term than actually getting it done.

27. Having excessive confidence in your own code

It’s dangerous to assume that just because you wrote something, it must be great. You learn more about programming as you work on new things and gain experience, so take a look at your old code from time to time and reflect on how you’ve progressed.

28. Not thinking about the trade-offs of each design, solution, or library

Every product has its fine points that you’ll only learn about by using and analyzing it. Seeing a few usage examples for a library will not make you a master of it, nor does it mean that it’s the perfect fit for every situation that will come up in your project. Be continually critical of everything you use.

29. Not getting help when you’re stuck

Keeping a short feedback loop will always be less painful for you. Asking for help doesn’t mean that you’re incompetent. The right people will see your effort and admission of ignorance as a drive to learn, and that’s a great virtue to have.

Testing and maintenance

30. Writing tests to pass

Writing tests that you know will pass is necessary. They will make refactoring and reorganizing a project much safer. On the other hand, you also have to write tests that you know won’t pass. They are necessary to move the project forward and keep track of issues.

31. Disregarding performance testing for critical cases

Prepare an automated performance testing setup at about the middle point of a project’s development process so you can make sure you don’t have escalating performance problems.

32. Not checking that your build works

It’s rare when a build passes but doesn’t really work, but it can happen, and it might be troublesome to fix the problem the longer you wait to look into it. Quickly testing every build is an important habit to have.

33. Pushing large changes late, or leaving after making a large push

This is where overconfidence will get you, and it can take getting burned multiple times to learn why you shouldn’t do this, so take my advice now and make sure you are always there when your build eventually breaks.

34. Disowning code you wrote

Be willing to support code you wrote. You are the most suitable person for helping others understand it. You should strive to make your code remain readable to yourself and others many years from now.

35. Ignoring the nonfunctional requirements

When you’re trying to deliver something, it can be easy to forget about some important areas such as performance and security. Keep a checklist for those. You don’t want them ruining your party because you drew up your deadlines without thinking about these nonfunctional concerns.

What are your worst programming habits?

As it’s often said, we are creatures of habit. Improving the way you work through habits is a great way to avoid having to think too much about every single situation. Once you’ve assimilated a good way of doing something, it becomes effortless.

Note: this post was originally featured on TechBeacon.

4 forgotten code constructs: time to revisit the past?

Some things in the programming world are so easy to misuse that most people prefer to never use them at all. These are the programming equivalent of a flamethrower: You might rarely be in the position to really need one, but every once in a while it turns out that you need to take down a forest. In that case, there’s no easier way than going Rambo on your codebase.

“There is no programming language—no matter how structured—that will prevent programmers from making bad programs.” —Larry Flon

That’s where a few of the old, forgotten code constructs come into play. Creative use of features such as goto, multiple inheritance, eval, and recursion may be just the right solution for experienced developers when used in the right situation.

In software development, one of the most important skills you can have is knowing how to evaluate tradeoffs. Once you get better at choosing the right way over the popular way, you’ll gain a lot in code clarity, readability, and reduced development time.

Sometimes the measure of a tool’s power lies more in the programmer’s ability to use it properly than on the qualities of the tool itself. Most of these forbidden ideas have special cases where they are not only useful, but they are the best possible solution available. That’s what they were made for. In those cases, you are missing out on a lot of power by not using them.

Here are four forgotten code constructs that should be resurrected.

Goto

Old codebases used to be infested with goto statements. In those times, you could use it to jump to particular code lines directly, by number. This led people to write the most inscrutable code imaginable, filled with numerical line references, akin to assembly language.

Because it’s so easy to misuse, it eventually started causing more problems than it solved. Then Edsger Dijikstra’s famous paper: “Goto Considered Harmful” came out and the rest is history. These days, people look at you funny if you try to argue that it can be useful.

Despite goto’s bad reputation, even today there are some popular projects using it, including the Linux kernel itself. For example, look at this intro to kernel development where the audience has a collective gasp when they hear about it. And then there’s a classic Linus Torvalds rant speaking in its favor.

The appeal behind goto was being able to write very tight loops, but the perils of this construct were not universally understood. One common mistake was to jump backward in code which, although easy to write, made the logic almost incomprehensible.

Few modern languages support goto. Of the most popular ones, only C, PHP and C# have it. But there are a few practical cases where it leads to code that is maintainable and easy to read.

Goto the right way

The main place where it shines is in error handling code. Here’s an example that can be a good alternative to having multiple consecutive if/else statements.

The reason why goto fits error handling well is that it gives you immediate visibility on the problem by flattening the code. The data handling and the clean-up code are separated and easy to see.

Bear in mind that this return value style with gotos is more suited for low-level code. In C++ you have the option of using RAII, and other high-level languages where return values are not common usually have exception support anyway.

Goto is also good for encapsulating repetitive return statements. Some methods have multiple exit paths that look very similar. In those cases, we can encapsulate the return statements and we’ll be able to easily change the output of the function without touching the data handling.

You could also encapsulate using method calls. That’s fine if your compiler inlines the resulting code, but as an added bonus in this case you also get to have the related code inside the same function instead of somewhere else in the file.

Multiple inheritance

This language feature lets you inherit a class from multiple parent classes to get functionality from all of them. Normally in OOP (Object-oriented programming) code you’ll only inherit from one parent, but in some contexts it seems more natural to inherit from many.

The danger here is complexity. Since you might affect multiple modules in your app from the same parent classes, it’s not that easy to reason about code changes. Any mistake could cause a chain reaction of bugs.

Unfortunately, misguided usage has made this once popular feature almost completely disappear. These days it’s almost synonymous with complexity in OOP, and most of the new languages have dropped it altogether. It can only be found in C++, Python, Lisp, and some other functional languages.

My gripe with multiple inheritance is that it’s an easy way to break the single-responsibility principle. Each class should be responsible for one task only, and once you inherit from multiple parents, things start to get muddy. In my experience, this principle tends to be thrown out quickly when deadlines and hard times come knocking, so I wouldn’t want to make that even easier.

Multiple inheritance the right way

On the other hand, sometimes you want to inherit from parents that are completely separate from each other. This is where multiple inheritance can become productive. Classes that have orthogonal behaviors are the best case scenario for multiple inheritance.

In this example, Swimmer and Flyer are completely separate abstractions, so inheriting from both is not going to lead to a mix-up of responsibilities in the child class.

Another good case for multiple inheritance is when you inherit from multiple interfaces instead of classes. This case is less dangerous, as inheriting does not bring additional functionality to the base class—it just extends it’s contract with the rest of the world.

I like to build interfaces that reflect a particular behavior. That works very naturally with multiple inheritance. For example, you can have a DatabaseDriver that is Queryable and Persistent, and maybe you’re implementing it as a MysqlDriver which has both behaviors working together.

As long as you are careful in having your abstractions clearly defined, multiple inheritance can be another powerful tool at your disposal.

Eval

Eval is a tool some languages have that lets you run arbitrary code from a string. Most interpreted languages have it, such as JavaScript, Python, PHP, and Ruby.

It is by far the most dangerous tool here, and that’s because you might accidentally give it a user’s input, which will open your system up completely. A malicious user would be able to run code to delete files or otherwise take control of your machine.

Despite this, in some cases it can be a very productive tool. Some problems become much easier to solve with eval, such as parsing a dynamic number of input variables and parsing template languages.

Although wanting to apply simple solutions like that is laudable, the dangers are too great. It is close to impossible to properly sanitize random input so that the eval call is safe from abuse, so it’s better to only evaluate code you’ve generated yourself.

Eval the right way

Evaluating your own code can still be very useful. Some examples include: interpreting JSON data, HTML templates, mathematical expressions, and detecting environment and language features.

Here’s an example for detecting if your browser supports ES6-style generators.

Recursion

More forgotten than forbidden, recursion is still in use in some settings such as in functional programming and in some libraries, although more common applications in an enterprise environment are not so easy to come by.

Functional programmers love recursion. Some functional programming languages such as Haskell even downright force you to use it, but the truth is that recursion is not natural outside of that world.

There are ways to make it work in the iterative world, though. One such way is through tail recursion. Simply put, you leave the recursive call at the end of your method, and the compiler will then be able to optimize your method to avoid stack overflows.

Unfortunately, some languages have decided to not support tail-call optimization. This is often true of web-based languages. From what I’ve gathered, both Python and PHP don’t support it, and JavaScript only does on ES6+ engines. In these cases, if you really need it, you can use something like trampolines to get around your environment’s limitations, but I’d recommend just sticking to iterative programming.

Recursion the right way

Recursion offers great simplification and code reduction in some algorithms. Particularly if you’re dealing with tree-based data structures and sorted lists, you’ll find that it’s much easier to implement solutions using recursion. In those cases, if you need simple code that works, go for it.

Performance-wise, it’s generally best to convert recursive functions to their iterative versions. The code will not be as easy to understand, but it’ll be more appropriate in production environments, as it will have more predictable performance.

Rediscover your language’s forgotten constructs

Knowing your language well is important. Even if you don’t plan on using these features, be sure to know the use cases where they are a good choice. Not only to have more options, but also to have a better grasp on the language itself.

Be pragmatic. If you haven’t read it yet, check out The Pragmatic Programmer. Knowing your tools by heart and all of their trade-offs is one of the marks of the experienced and battle-hardened developer.

Note: this post was originally featured on TechBeacon.

How terrible code gets written by perfectly sane people

When I found out I would be working on porting an old Python codebase to Node, I was slightly excited. These kinds of projects always give you more creative freedom than the ordinary code maintenance gig, and something about the challenge of rewriting other people’s code makes it fun as hell.

The excitement significantly faded once I actually got a look at what we were going to work with. Legacy code can be nasty, but I’ve been programming for 15 years and only a couple of times had I seen something like this. The authors had created their own framework, and it was a perfect storm of anti-patterns: no separation of concerns, mixed spaces/tabs for indentation, multiple names for the same concept, variables being overwritten by the exact same data coming from a different yet nearly identical method, magic strings… this mess could only have been the product of a room full of babbling monkeys copying code randomly from Google.

And yet, it was not the code’s dismal quality that piqued my interest and led me to write this article. What I discovered after some months working there, was that the authors were actually an experienced group of senior developers with good technical skills. What could lead a team of competent developers to produce and actually deliver something like this? What I’ve come up is a list. These are some bad habits that even experienced teams can get into which will severely affect your end product, more than any static code checker or development methodology could rescue it from.

Update: Mia Li was kind enough to offer a Chinese translation of this post here.

Giving excessive importance to estimates

An important component of this project was the focus on deadlines, even to the detriment of code quality. If your developers have to focus on delivering rather than on writing good code, they will eventually have to compensate to make you happy. The two ways in which they can do this are over-estimating and over-promising, and they both come with added baggage.

Typically it’s more difficult to over-estimate because the effects are immediately evidenced on project cost, so your developers might choose to over-promise instead, and then skip important work such as thinking about architectural problems, or how to automate tasks, in order to meet an unrealistic deadline. These tasks are often seen as added value, so they get cut without notice. Product quality will decline as you accumulate technical debt, and you’ll find out about it later than you’d really want to, because reorganizing code later in a project costs exponentially more.

As an example, on this project I would find code that was obviously duplicated elsewhere, but it seemed that people were in such a rush to deliver that some developers would not bother to check if someone else had written the same method or SQL query before.

Sometimes estimates can even be deceiving. For example, Agile has a term called “velocity”. The idea is to calculate how fast your team can deliver, and make the necessary changes to go faster. The problem is that it’s not possible to come up with an accurate velocity in the short to mid term. The law of averages says that you can’t look at past performance to gauge how fast you can go right now, because past performance is not a good indicator of future results.

The law of averages is a layman’s term for a belief that the statistical distribution of outcomes among members of a small sample must reflect the distribution of outcomes across the population as a whole.

In truth, a developer can write a large amount of code one day, and she can take three days to write five lines of code after reading documentation and collaborating with teammates. Averaging estimates will not net you valuable information in the short or mid term.

Giving no importance to project knowledge

As your project progresses, your team learns about the business, the concepts behind it and how they connect together. They also learn about the implementation as they write code, because you can never fully visualize how things will turn out and which challenges you will face. Some business fields even have an inherent complexity that takes a long time to digest.

As this was a full rewrite of old code, it was particularly interesting in this regard, because it serves to show whether your team’s management understands project knowledge and how it impacts development. If you’re in a large project and there are modules for which you have no expert, no-one to ask about, that’s a big red flag.  The value in rewriting code rests entirely on taking advantage of what you learned the first time around, so value that knowledge dearly.

If you put a different team to do the rewriting, as was done in my case, you are ignoring all your learning and relying solely on your new team’s skills, which likely won’t make up for the lack of information. A mediocre developer is going to do a better job at rewriting something he wrote himself, and he’ll do it faster, than if you give the job to someone else entirely.

Even hiring is impacted by project knowledge. The more information the project carriers, the longer it will take to bring people up to speed, so not only is domain knowledge more valuable then, it’s also important to focus on hiring good people. If you don’t hire well, you’ll get tied up to a bad team that will go nowhere for months.

Focusing on poor metrics such as “issues closed” or “commits per day”

When a measure becomes a target, it ceases to be a good measure.
– Goodhart’s law

At some point while I was getting up to speed on this project, somebody asked me why another developer was closing issues much faster than me, as if delivering faster is a good thing. As you can imagine, it took me a glance at his code to find four bugs in a single line. Focusing on unreliable metrics such as this will completely derail your project, and cause people as much stress as deadlines.

One metric that few seem to focus on is regression rate of issues. There are bugs such as null pointer exceptions that might show up much later, and if you’re not tracking regressions, it will seem as if bugs keep springing up out of nowhere. In this situation, you’ll never find the source of your problems, because you’re looking in the wrong place.

Ultimately what matters is what is delivered to the client, how happy they are with the product, and how it affects their bottom line, but it takes a lot of self-control to focus on delivered quality and ignore juicy metrics such as commit rate or issues closed.

A good way to know if a metric is useful or not, is to try to understand what personal values it outlines. Concentrate on metrics that advertise good attention to details, good communication skills and good attitude, especially if they require great effort to cheat.

Assuming that good process fixes bad people

Good process is portrayed as a kind of silver bullet in business. In my experience some companies, especially large ones with a poor hiring methodology, end up making their process increasingly strict to put toxic people in line, in turn restricting the creative freedom of the ones carrying the team. Not only that, but you still need people to carry out your process properly in the first place for it to work.

It never ends, and this entire problem can be disregarded by just fixing the hiring. Talent makes up for any other inefficiency in your team. That’s the entire point of favoring smart work over hard work.

Developers can be especially tricky to communicate with. In such a complex codebase, I had to constantly ask others for help, and would not often have people happily set time aside to give me a hand. That does not reflect a good attitude, and in tough tasks things can get especially stressful if you have to ask for help and can only count on a few of your teammates to have both the knowledge and the will to give you a hand.

You need people who can apply common sense and good taste, and who will call you out if your process is holding them back. Every build tool, every static checker and every communication tool has good and bad use cases, and you need people to tell you about it, not to blindly apply something that looked good in a different context months ago.

Ignoring proven practices such as code reviews and unit testing

Staying up to date on modern software development processes might not be enough to put a derailed project back on it’s tracks, but it’s certainly necessary anyway if you want your team to stay competitive. This is where proven practices step in, and there are a few of them. Test-driven development has been shown to reduce defect rates 40% to 90%, with an increase in development time in the 15%-35% range. Code reviews have also been shown to decrease defect rates, in some cases up to 80% over manual testing.

Imagine my dismay when I had to collaborate with a colleague on that legacy project and his screen displayed Notepad in its full glory. Using “search” to find methods might have been rad back in the nineties, but these days, refraining from using tools such as modern IDEs, version control and code inspection will set you back tremendously. They are now  absolutely required for projects of any size.

For an in-depth look at what is proven to work in software development, check out the book Making Software: What Really Works, and Why We Believe It. It has a nice list of valuable and proven practices which have been backed by studies over the years.

Hiring developers with no “people” skills

It’s not that developers can’t talk to other people. I was once a shy developer myself and eventually managed to stand in front of an audience and give a talk just fine.

The problem comes when someone isn’t willing to even try, or becomes annoyed by requests to improve communication. If there’s one thing that can speed up development time more than anything else that I’ve mentioned, it’s improving communication. Particularly when you reduce distances and work on informational proximity, you enable a more fervent and articulate connection in the workplace. It doesn’t matter that the other guy might be ten thousand miles away. One Skype call can turn a long coding marathon into a five-minute fix.

Conclusions

When you enable and encourage working smart by using the best tools, proven techniques and great communication, software development will definitely flow more naturally. What you can’t assume is that just because you’ve signed up to apply Agile or some other tool, that nothing else matters and things will sort themselves out. There’s a synergistic effect in action here which can make a team exponentially more productive if they’re set up right, and terribly slow and sloppy when no attention is paid to the details.

Note: this post was originally featured on TechBeacon.

Page 1 of 3

Powered by WordPress & Theme by Anders Norén