209: The TDD Advantage
Understanding the relationship between DRY, TDD, Object-Oriented Code and flexible systems
Thank you for reading Snippets of Text. This is a preview of a post available exclusively to paying subscribers. You can get unlimited access to all articles by purchasing a subscription.
Unrelated: The DRY Principle
Making a system DRY is difficult because we cannot assume that every part of identical code is duplicated. Instead of waiting for duplication to show up at any time, it is better to take that as soon as we see the same code again. That duplication should be removed. The third time is when you remove it.
Code tends to propagate within a team. You are assuming that we will remember that a piece of code could be done through comments and `#TODO,` why not remove it right there?
Defining roles allows for separating concerns even at the infrastructure level. A system's behavior defines its role. Separating concerns based on behavior promotes the DRY (Don't Repeat Yourself) principle. This granularity enables the swapping of components for better ones.
A system's structure determines how easy it is to adapt the system to new requirements in the short and long term. Most teams accept the suggestions provided by frameworks and other libraries. Yet, frameworks conventions are suggestions, not rules. How software is structured can impact our ability to adapt and evolve it, even in the short term. The question isn't whether programmers are doing design or not. The question is, are you going to regret your design choices later? Thus, any new change should move the system towards a more straightforward design.
Every change introduces in a system implies making changes to the design. Every programmer is a designer. The experience level could be more relevant, but the structural changes made to the application. The person with the hands on the keyboard is changing the design. You're always doing design every time you write code.
Off Topic: How to Write More Encapsulated Code in Ruby
Programming involves using fancy words like polymorphism, encapsulation, and coupling, which may only sometimes make sense. It's important to avoid asking objects for information and perform a function for them. Instead, we should command objects to do what they need to do. Each object is responsible for its part of the domain and should tell the next object what to do. Encapsulation should remove the ability to add if statements, forcing us to use commands instead of queries. This prevents us from putting processing in the wrong place and ensures that responsibilities are kept in different objects.
To better encapsulate an object, hide data access behind methods and limit external access to the object's internal state. Start each class definition with private methods to identify if the object is doing too much.
The object that owns the data can decide what should be displayed and not how. A value object is intended to be queried, and only that you don't do anything else with it. It simplifies our parameters. East orientation decreases coupling and increases code clarity, cohesion, and flexibility. You can look at your code and see the direction of information, and if things are traveling west, that's an indication that you're leaking information, right you should see it like this. At least information leaks knowledge. It leaks responsibilities. Their queries actively encourage you to put code in the wrong place.
Commands encourage polymorphism. Eastern encode, and commands enforce encapsulation. They push responsibility down into objects. It is a loosening coupling. To be east-oriented, always return self objects can query about themselves factories are exempt break the rules sparingly like in value objects. If you're returning self and hiding information about an object so that you cannot query it, then there's no other option.
Factories do not need to follow the same rules as other objects because they are not instances of any object and have no internal state. Therefore, there is no instance in memory, and there is no preserved state within the object.
Encapsulation refers to grouping data with the methods that manipulate that data. Encapsulation conceals an object's information, represented by its data or internal state. This is important because it increases coupling when entities access each other's internal data. The concept of "Tell, don't ask" is central to information hiding, but it can be challenging because it assumes that an object should always know what to say or answer.
In Ruby, a practical way to enforce encapsulation is to always return "self" from every method that modifies the state. By doing this, the last statement executed by the method will be the return value in Ruby. This improves encapsulation by defining clear boundaries between objects and reducing the risk of exposing internal structures. Objects should be owners of their data and responsible for disclosing methods to access that data.
Current Work: The TDD Advantage
Testable systems ensure adaptability for future changes. Yet, having a testable system does not mean the design is robust. It depends on the TDD practice knowledge of the code creator.
The primary benefit of TDD is not adding more tests for the sake of it. The focus is on breaking down the problem into manageable pieces that can be analyzed. The essential advantage is that the design can only improve by creating more tests. A better design is measured by comparing two solutions to the same problem. It is wrong to assume that a system has a better design because it has unit tests. This could state various issues related to application design.
Thanks for taking a look at the free preview of Snippets of Text. Please consider subscribing to the paid version if you find my work helpful. This way, I can spend more time developing new ideas to share with you.
Keep reading with a 7-day free trial
Subscribe to Snippets of Text to keep reading this post and get 7 days of free access to the full post archives.