David Scott Bernstein's Blog, page 21

May 10, 2017

Quality Code is Loosely Coupled

While cohesion, which was the subject of the last three posts, is about the internal consistency of a class or method, coupling is a code quality that looks at the relationship between entities.


There are two distinct forms of coupling: loose coupling and tight coupling. One is good, the other isn’t—and I always forget which one is which. They both sound pretty bad to me so I prefer using the terms intentional coupling and accidental coupling instead.


The best kind of coupling is part of the domain model. We want the right kind of coupling in our systems. We’re not striving for systems with no coupling at all. A system with no coupling is a system that does nothing! Instead we’re striving for the right kind of coupling, one that’s needed to create the right kind of behavior but no more.


I’ve found that bad coupling usually gets into a system as a secondary effect, the primary cause of which is that one of the other code qualities are missing.


For example, when classes are not cohesive they have many reasons for clients to couple to them. This type of approach, which is a common practice, causes systems to be tightly bound together making it very difficult to change and to extend.


Loose coupling is about making external calls indirectly through abstractions such as abstract classes or interfaces. This allows the code to run without having to have the real dependency present, making it more testable and more modular.


One of the best known techniques for working with external dependencies is dependency injection. Dependency injection is simply the practice of separating the instantiation of a service from its usage. If you want to use a service, then instead of instantiating it and invoking it, request that another entity instantiate it and then give you access to it because then you can be passed a mock instead of the real object, which will help facilitate automated testing.


It’s really that simple act of injecting dependencies rather than instantiating them that lets us automate the testing of our software.


I want my coupling to be explicit. I want to keep the relationships between my entities clear and intentional, using strong contracts. This helps me avoid a whole range of errors and also nicely partitions my system.


We want coupling to reflect the nature of the problem and if we model a system well then each class only knows about what it needs to know about. This is how we limit knowledge in a system. The reason we want to limit knowledge in a system is that it limits points of failure and reduces the impact of change on a system.


Generally speaking, the more loosely coupled the system is, the more flexible it is. Sometimes we need maximum flexibility so we look at patterns like the Observer Pattern or the Vsistor Pattern. Other times we don’t need a lot of flexibility so we opt to build software in such a way that it reflects the structure of what it is we’re modelling.


Coupling is all about the relationship between classes and we only get a handful of relationships that we can have between classes. One class can contain another class, which in languages like Java and C Sharp means that one object has reference to another object. These languages also have the notion of inheritance where one class can inherit from another class. Classes can also implement a common interface.


These are the primary ways in which classes can relate to each other. There are only a handful, so using the right ones in the right ways is absolutely critical.


Does a package have an address or does an address have a package? We don’t give these things a second thought in the real world, but in the virtual world we make the rules, so we have to be explicit.


I want coupling where I intend it to be and no coupling where I don’t intend it to be. It’s that simple in theory, but in practice… well, that’s another story.

 •  0 comments  •  flag
Share on Twitter
Published on May 10, 2017 10:47

May 3, 2017

Cohesion and Testability

I feel like I try to go around and get developers to do little things that will make a big difference in their work.


For example, if you need to add some behavior to your system, rather than putting that behavior into some pre-existing entity, you should probably create a new class for that new behavior. I like to keep my classes and methods highly focused on one thing so they tend to be rather small, encapsulating no more than a handful of lines of code. I do this primarily for readability so a person can read a method that delegates to other methods and understand the overall flow of what’s going on.


Because some of the methods I write simply delegate to other methods, and most of those methods are private methods, I’m often asked how I test them and the answer is that typically I don’t have to test them directly. If I can exercise the behavior that they evoke by calling test on the public interface of the API then I’ve tested the behavior of those private methods.


I can learn a lot about the cohesion of a system by hearing what people say about how difficult it is to test that system. For example, if someone says to me, “There are too many permutations to test this class,” then I know they probably have cohesion issues.


If a class has only one issue then it requires only one test, but a class with two issues requires two tests, and a class with three issues requires four tests. A class with four issues requires eight tests, a class with five issues requires sixteen tests and so on. It grows exponentially.


But five classes, each with only one issue, requires only five tests (plus integration). So what would you rather do? Create five classes and five tests or create one class and sixteen tests. I think most of us, when we think about it and if we value testability, will pick the first option.


Typically the first place I look in order to make software more testable is extracting out behavior and entities so classes have well-defined behavior. That lets me give them better names so they’re easier to understand and work with in the future.


One of the most important things in making code testable is making it more cohesive. Smaller behaviors are easier to test than larger behaviors. They also give more meaningful feedback, which makes the system more reliable. When classes are more cohesive, their tests become far simpler to write and test scenarios become more straightforward.


Classes can represent anything. They can represent concepts like an insurance policy or they can represent tangible things like the terrain of a map. If we can name something, we can represent it with a class, and classes are often made up of other classes. Classes can also represent relationships. Really anything we can imagine can be represented by classes. It’s a powerful tool.


But we often build classes that are way too big and have other classes lurking within them that should have been broken out into smaller classes. Smaller classes are easier to test and they are also easier to understand. This drops the cost of ownership for software.


My rule of thumb is to keep methods short and method names expressive. I write tests for the behaviors that I build, but when I’m refactoring code and extracting methods I don’t typically write tests for those methods if the behavior hasn’t changed and as long as the test for the behavior that calls that private method can exercise its behavior. So the private method just tested indirectly, which is typically fine. Remember, the only reason the private method exists in the first place was to call out what the behavior was doing. This is a form of documentation, really. I much prefer to extract a bit of a behavior into its own method and give it a meaningful name than write a comment on what the code is doing. That’s so 1980s!


Valuing testable code has made me pay great attention to cohesion, which in turn has helped me manage complexity in my code and make my code easier to work with.

 •  0 comments  •  flag
Share on Twitter
Published on May 03, 2017 10:25

April 26, 2017

Pathologies of Uncohesive Code

Uncohesive code is code that either has too many responsibilities or has poorly defined responsibilities. Typically, it’s code that tries to do too much.


Poorly cohesive classes are difficult to understand and maintain. They’re harder to debug and more difficult to extend. The bottom line is the more issues a class has to deal with, the more things can possibly go wrong.


Classes that have poor cohesion end up becoming overly complex. They couple multiple issues together that shouldn’t necessarily be coupled together.


Classes become uncohesive when we don’t take the time to call out the entities for which the behaviors belong when we’re building out behaviors. You might think this is no big deal but it adds up. Without calling out in the domain, the object model becomes distorted and it becomes harder to understand and maintain in the future. And my number one goal when writing code is to communicate to other developers what it is I’m doing, so building out the object model and naming the elements well is fundamental to building good software.


One way to create cohesive classes is to look for behaviors that share or affect a common set of data. The common set of data can become instance data for the class, and the behaviors can be methods on the class. If cohesive classes are about having a single responsibility, then cohesive methods are about fulfilling some functional aspect of that single responsibility. Methods are the behavior of classes and they should be clearly defined.


Poorly cohesive methods are methods that may have overly generalized interfaces. For example, many years ago I worked on the OS/2 operating system and we believed that having a generalized API was a good thing. It minimized the number of APIs you needed to provide and it seemed to be making more efficient use of memory, which was quite expensive back then.


Today I believe just the opposite. Overly generalized method interfaces give orthogonal clients access to the same APIs. This means they become a coupling point for many different types of clients and can create side effects between different clients and couple them together in unexpected ways. Methods that try to do too much are also very difficult to use.


Poorly cohesive methods are also very difficult to refactor. Extract Method is the most common refactoring that I see aside from Rename, which is my favorite refactoring because it’s safe and it embeds knowledge into the system. Extract Method is my number one defense against poorly cohesive methods, which are simply methods that are trapped within a loner method. If I can name what the behavior does then I’ll extract that behavior into its own method and give it that name. I find that this makes my code far easier to read and understand. I much prefer to delegate to a private method with a meaningful name than to insert a comment explaining what the block of code does.


Sometimes bad things can tell us good things and I think this is true with cohesion. When we look at the price we pay for poor cohesion in code, we see that it’s simply not worth it. It’s far better to take the time and craft method interfaces for specific types of clients and to craft our object models with entities that are focused and singular.

 •  0 comments  •  flag
Share on Twitter
Published on April 26, 2017 11:44

April 19, 2017

Quality Code is Cohesive

The first code quality we’ll discuss is my very favorite: cohesion. Cohesive code is highly focused code that’s straightforward and easy to read. I like cohesion because it’s the easiest one for me to spot, but I recognize that that’s not true for everyone. People have told me that cohesion is actually the hardest for them to spot. If that’s true for you then fear not because you don’t have to be an expert at every one of these code qualities. Focus on just one or a few of them and as one improves, you should see others improve as well.


I’ll give you some tips in this post on how to identify cohesive classes and some tips in the next post on how to identify uncohesive classes.


When developers talk about cohesion, we’re talking about how focused the entity is because we want to make each entity in our system about one thing and only one thing. When something is cohesive, it’s about one thing.


Another way to see cohesive code is as fulfilling the Single Responsibility Principle. In other words, it has one and only one responsibility.


The benefit of this, of course, as Bob Martin points out, is that an entity that has only one reason to exist also has only one reason at most to change. So cohesion in code gives us the ability to limit the amount of change to code, or rather to spread changes out among multiple  objects in the system.


Cohesion in code implies a lot. It implies that you will have lots of little entities rather than a few large ones, but each entity will be highly focused and if there is a bug in behavior it’s usually due to the entity with that responsibility—so it’s easy to find.


Cohesive code is also easy to read. Since each entity is about one thing, we can name it for what it does. In fact, this is my litmus test for whether a class or method is cohesive or not. I simply ask myself, “Can I name it in a way that describes precisely what it does?” If I can, that entity is cohesive. If I can’t easily name it or if my name has the word “and” or an under bar in the middle of it then I know that my entity is about more than one thing. So naming for me is my first line of understanding whether or not what I’m building is cohesive.


Quite frankly, I used to be scared of programs that had lots of little classes because it always seemed like the behavior was somewhere else. When I try to trace through this kind of code I find myself jumping around all over the place, thinking there are too many levels of indirection going on. The reason I feel that way is that I don’t understand the value and benefits of this type of programming.


As a structured programmer, I was familiar with tracing through code. I pretended that I was the CPU and I would execute lines of instruction in sequence. This is how I was trained, many years ago, to read and understand sequential programming. But this approach doesn’t work very well for object-oriented programs so I take a different approach.


I think about good object-oriented programs as having layers I can use to understand my program from different perspectives. Understanding a program in this way allows me to compartmentalize behaviors. For example, if I’m writing a program to generate a report and there’s a problem with the report’s header, I know exactly where to look in the code to find the issue, namely in the code responsible for generating the report’s header. I don’t have to look in the footer code or in the pagination code, and so in this way bugs are isolated, making them more straightforward to find and fix.


When the objects in our object model are cohesive, it’s telling us we understand and have represented the domain correctly. Cohesion is the number one defense against complexity, and complexity is the mind killer. When we write complex code, it’s hard to follow and it becomes difficult for others to maintain. Cohesion allows us to manage complexity by putting each thing in its own place thereby limiting how they interact with their defined interfaces. This is the essence of all good architecture and design.


I feel that cohesive code goes right to the core of what good software is all about. Its responsibilities are focused and it’s easy to understand.

 •  0 comments  •  flag
Share on Twitter
Published on April 19, 2017 08:20

April 12, 2017

Keep it CLEAN

I like dirty martinis and R-rated movies but my code is one place I always want to keep CLEAN.


CLEAN is an acronym I use to remind myself of the five key code qualities that I’ve found to have the biggest impact for improving the maintainability and extendibility of code.


Before I describe exactly what they are I just want to say that this is certainly not an exhaustive list, and it may not include all the qualities you look for in code.


But these are the ones I’ve found to be the easiest for me to call out and talk about with other people. They’re also the ones I’ve found to have the highest value.


For me, software quality is all about dropping the cost of ownership.


Quality in manufactured goods helps extend the longevity of the product. Higher quality products last longer. The same is true with software. Higher quality software should be able to last longer because it’s more fit for its purpose and it’s built to be extendible. So when the customer inevitably comes back with requests for improvements, the development team can implement those requests and make the customer happy. It may be a simple definition of quality but it’s a useful one.


And these qualities are quantitative. We can measure them in code. This makes them useful for helping us arrive at a consensus for what principles and practices to follow. They give us a rubric for evaluating basically everything we do in terms of writing code and building features.


And these qualities aren’t just present in code, we see them everywhere in the world around us—in the way we communicate, in our literature, and in the media we consume. They’re everywhere because they reflect how we like to process and understand the world.


I’ll cast these code qualities in terms of object-oriented programs because that’s what I’m most familiar with but these qualities are also present in languages from assembly to COBOL. Regardless of the programming language, we have the ability to create independent units of execution either as objects or as processes, and the more sophisticated the language the more capable we are at being able to create well-defined entities.


CLEAN relates to the consistency and relationship between entities. These should have well-defined characteristics, that is they should be focused in terms of their composition and in terms of their relationships. They should hide implementation details. They should treat each other autonomously and be defined in only one place.


We have names for each of these things and here they are:



Cohesive.
Loosely coupled.
Encapsulated.
Assertive.
Non-redundant.

The first letter of each of these words spells the word CLEAN. This is my acronym for code quality. I want my code to be CLEAN.


CLEAN code is also a shout-out to Uncle Bob Martin’s book Clean Code: a Handbook of Agile Software Craftsmanship and to Misko Hevery’s The Clean Code Talks: A Google Tech Talk. These are both great resources for developers. There really aren’t many places to go to learn software development beyond the basics. I think 99% of all the books out on software development are beginner books, and it’s really hard to find good material for professional software developers who are looking to further their craft. These are both excellent resources for developers waning to improve their craft.


But our discussion of CLEAN code won’t touch on either of these resources because I’ve done what we developers are great at doing: I’ve overloaded the term to mean something else entirely.


Before you throw tomatoes, virtually speaking, I’d just like to point out that this is how we make concepts memorable and understandable. Software development, and language in general, is full of overloaded terms and yes, there is room for one more.


So there it is: Write CLEAN Code. In the next several posts let’s talk about what CLEAN really means.

 •  0 comments  •  flag
Share on Twitter
Published on April 12, 2017 10:35

April 5, 2017

Quality in Software

Quality is a word we throw around a lot, but without much consensus on what it really means. And it can mean different things in different contexts. Quality in software is very different—the word has a fundamentally different meaning—from quality in goods or services. This is because software is fundamentally different than goods or services. Software is a little bit of both, and something else entirely.


When most people talk about quality, they’re thinking about the quality of the experience. For example, quality in software means it has a good and consistent user experience, the program runs, is easy to use, is free from defects, and so on. These are external qualities of software. But there is another set of qualities I would rather focus on: the internal qualities of software, the qualities related to the code itself. Internal qualities are causal and measurable, are actually quantitative and actionable so as you improve these qualities, the overall quality of your code also improves. And being able to measure specific qualities in software gives us the ability to evaluate aspects of it and identify what code supports maintainability and what doesn’t.


When I was writing Beyond Legacy Code: Nine Practices to Extend the Life and Value of Your Software, I wanted to write about the most valuable Agile practices I’ve used, but these were technical practices which are rarely written about for a non-technical audience. It’s easy to talk about what good practices are but a little more difficult to talk about why they’re valuable so we can really understand them and use them to their fullest.


But I had a challenge.


I can introduce people to some basic concepts, but how do I communicate an understanding of what good code actually is and why it’s important to write good code in terms of the ability to maintain and change the code in the future.


I’ve studied and taught advanced software design for many years and to do so I draw on a range of a well-known principles and practices. I’m a vocal proponent to design patterns as well. But sharing that information would take volumes. Somehow I had to convey the core principles for developing maintainable software in less than twenty pages, which all the room I had for it in my book. So instead of focusing on theory and principles, I decided to focus on five key code qualities that tend to be present whenever we use good principles, practices, and patterns.


These five qualities of code are independent of the language being used. Some languages, particularly object-oriented languages, make it easier to apply these code qualities but they exist or don’t exist in all code regardless of the language being used.


I think of these five code qualities as fundamental. You may be familiar with the terms from college textbooks but I would like to go deeper than those texts would normally go, so over the next several blog posts I’ll be discussing little code qualities that make a big difference in terms of the maintainability and extensibility of software.


These five code qualities are the keys to dropping the cost of ownership for the software you build.


By the way, I‘m not talking about the so called “quality factors” or “–ables” as I call them. For example, reliable. If I tell you your code should be reliable I haven’t helped you very much because I haven’t told you how to make it reliable.


So then what are these five magical qualities in code? Well, I’ll answer that question in my next blog post so stay tuned and I I’ll see you next week. Until then, share what works for you!

 •  0 comments  •  flag
Share on Twitter
Published on April 05, 2017 12:04

March 29, 2017

Beyond Waterfall Software Development

Developing software in short sprints requires a different approach than traditional software development, though it seems strange to call something only a few decades old “traditional.” Winston Royce coined the term “waterfall software development” in 1970 and he said in that very same article that we shouldn’t use it because it doesn’t really work.


Many of the things that we recognize as poor software development practices seem to have been fostered in a Waterfall environment, such as specialization, siloing of knowledge and teams, and more importantly, creating an environment where software doesn’t need to be changed and so little attention is put on writing code that can be changed. Then, when the inevitable happens for successful software and changes are needed, it becomes extremely expensive to provide them.


We think about software in a lot of ways by relating it to tangible things, because we cannot truly visualize it or understand it. We think about software construction like physical construction but they are actually worlds apart both in the skills you need and the way you approach the problem. This should be no surprise because the forces at work in each problem domain are entirely different.


In physical construction, the forces are the laws of physics but in virtual construction we don’t have the laws of physics. However, we do have physical law and here’s the distinction: We work from a predefined system that includes binary logic, predicate calculus, propositional calculus, functional programming, object-oriented programming, and much more to come, I’m sure. Our very understanding of modelling the world is evolving and it’s showing us a model for how we think and understand the world in our own minds and giving us ways of seeing ourselves we could never have otherwise experienced. For me, this is what’s most exciting, seeing the relationship between consciousness and software because it in itself is the little microcosm that we can look into and see another aspect of ourselves.


The Scrum Software Development Essentials Training is about learning by doing. Participants experience what it’s like working and developing valuable software in Scrum teams through a series of guided exercises. Doing these exercises, you’ll get a chance to apply the practices of Scrum and Extreme Programming as well as object-oriented analysis and design patterns to software development problems.


Over the course of three afternoons, you’ll build a software application using test first software development, pair programming, refactoring to patterns, and apply a variety of advanced techniques. These are the same practices that will help you master Scrum software development. Please contact me if you’d like to bring this training to your team.

 •  0 comments  •  flag
Share on Twitter
Published on March 29, 2017 11:35

March 22, 2017

Finding the Right Metaphors

In his book, Domain Driven Design, Eric Evans said that there really is no such thing as complexity, complexity happens when we diverge from our models, when we use metaphors inconsistently, and I tend to agree. Very involved systems can be easy to understand as long as they adhere to an appropriate set of metaphors.


So how do we find the right metaphors for the problem at hand?


It’s actually easy, because we naturally think in metaphors to begin with. Any problem we express is already defined in some form of abstraction or metaphor, so our primary task is to group different pieces together in ways that make sense to us. We do this by simply asking, “What is the same about these different pieces?” and “What is different about them?”


If we think from the user’s perspective we can often get a good sense of the domain model and then drive down to implementation. Design patterns can be used to group concepts in ways that support our goals.


I have an architectural pattern I use related to this that I called “the three layer DIP” (DIP stands for dependency inversion principle). The application or domain layer is the top level, below that is the services layer and below that is the platform layer. The idea is that the domain layer only makes calls to the services layer and the services layer only makes calls to other services or the platform. For many kinds of applications this is a good framework to work within because it makes the domain model clear at the application layer.


The metaphors we use in an object-oriented system are composed of objects. Every object in the system should have its own perspective in the world and should know only enough to accomplish its specific tasks. Separating out perspectives in software gives us the ability to hide different parts of the system so we can make changes without those changes rippling out into other parts of the system. This limiting of knowledge gives software the ability to be more changeable.


Of course, metaphors mean nothing to a computer but the primary goal of writing software isn’t to make a computer do something, that’s secondary. The primary goal of writing software is to communicate our designs to other developers. Since the dawn of civilization, metaphors have been one of the most powerful ways to communicate ideas. Incorporating metaphors in our software designs helps us communicate complex ideas in our code.

 •  0 comments  •  flag
Share on Twitter
Published on March 22, 2017 15:17

March 15, 2017

Perspectives

Our job as developers is less about being clever or finding efficient algorithms and more about simply perceiving the problem and representing it as accurately as possible. This is an exciting shift in software development because it means our goal is really about understanding. Once we understand a problem everything else can fall into place easily. This is why I put so much value in problem-solving techniques for software developers because I know it’s the key characteristic that great developers possess.


One of the most valuable lessons we’ve learned from the object-oriented software development movement is the notion of perspectives.


There are many kinds of perspectives that we can adopt in software: caller-callee, client-server, and many others. We can even have layers of perspectives that deal with a problem at different levels of abstraction. This makes software much easier to read and understand, and because we already have the right abstractions in place, it makes it easier for software to be extended.


There are two perspectives that are critically important when building software and the interface between them often determines the quality of the system being built. I call these two perspectives domain and services.


The domain perspective has nothing to do with software. It is a representation of the domain, of the way subject matter experts think about their domain, and therefore is often rich with metaphors and a unique vocabulary. Understanding the domain and being consistent with it has a huge impact on how elegant a solution we can come up with.


Designing a system around domain entities is often the best choice for creating software that addresses the customer’s needs now and in the future. Divergence from domain metaphors can lead to overly complex software that’s difficult to stitch together.


The services perspective is a way of grouping the capabilities of the computer system in such a way that we can support the domain model. Rather than having the domain layer make calls to system APIs, we prefer to package the services on the system into entities that can be directly consumed by the domain model. These two layers of abstraction can solve some fundamental problems we encounter in building and testing code.


The point at which these two perspectives interface with each other can be the ideal place for automated tests. Testing the services layer allows us to test implementation without being implementation-dependent. It can give us a “seam” that allows us to add tests without the need to mock out a lot of dependencies. Domain tests can act as integration tests, weaving together different services.


Building systems in this way can lead us to think about problems the way subject matter experts do, and this gives us a system that’s more flexible to the needs of the particular domain.

 •  0 comments  •  flag
Share on Twitter
Published on March 15, 2017 09:55

March 8, 2017

Avoiding Integration Hell

Perhaps the most important yet easiest to implement of all the software development practices in Agile is continuous integration. Continuous integration is simply creating an infrastructure where the code that is being built can be integrated into the project immediately.


One of the biggest challenges when writing software is the dependency relationship between the code that’s been built and the rest of the code in the system. These dependencies are not always obvious and only show up when we begin to integrate code from various developers into the entire project.


In traditional Waterfall development, software is integrated as one of the final steps of the process. But doing so obscures many of the potential bugs and often ends up in “integration hell” that delays projects and compromises code quality.


This can all be avoided through continuous integration. Bugs are found while they’re still fresh in the mind of the developers who wrote them so that they’re easier to fix and they don’t get compounded by other bugs through the development process. The sooner we can identify and fix bugs the cheaper it is to do so.


A feature is not considered done until it runs and passes all tests on the build server. Just getting a feature to run on the machine of the developer who wrote it can hide a lot of problems that otherwise wouldn’t be uncovered until integration.


But something magical happens when developers use continuous integration. It changes the way we approach problems. We can immediately see if our standards aren’t strong enough or if our approach isn’t right for the problem by integrating our code with the rest of the system as it’s being built. It teaches us in the only way we really learn, with immediate feedback. As a result, the team just naturally flows to building high quality software and there are far fewer surprises at the end of the project.


Continuous integration is easy to implement. Most of the tools needed to make it work are free and relatively painless to set up. Despite this, I’m always surprised to find that many development shops do not use continuous integration, even the ones that call themselves “Agile.”


Continuous integration also creates the technical infrastructure for other Agile practices such as running a suite of unit tests and incrementally building features. I don’t think it’s possible to have an Agile project without continuous integration and if you do no other technical practices I highly recommend continuous integration as the first and foremost Agile technical practice. You will significantly reduce the risk of your project failing and be able to measure progress much more accurately when features are fully integrated into the system as they’re built.

 •  0 comments  •  flag
Share on Twitter
Published on March 08, 2017 11:44