Skip to main content

Posts

Showing posts from 2015

Year in Review 2015

JanuaryStarted the year with reasons as to why technical blogging is useful and recommended.Kick started a series of posts themed around simplicity, recommending to abstract data use and not data access.FebruaryA series of posts on abstractions.The power of Value Objects as a key refactoring.MarchConcluded the series on abstractions.The Dependency Elimination Principle and Reused Abstraction Principle have been two of the biggest changes I've made in my coding habits this year.AprilA follow up to one of my previous posts around the use of getters and setters.I realised I had failed at maintaining and managing URIs for the content on the site.The end of the month saw a focus on two simple topics that can clean up duplication in tests and remove obscurity from assertions.MayA focus on tasking and planning in software development.Two core techniques of producing a Walking Skeleton and Mob Programming have proved invaluable.JuneProvided two techniques that allow continuous delivery of…

Recommended Reading List 2015

In a similar manner to previousyears here is my recommended reading list for 2015. As before some of these books have been released for a while, but these are some of my highlights for various reasons.CodeDependency Injection in .NETI had put off reading this book for a while despite numerous teams having a copy. It turns out a good chunk of the book is applicable to any language, with the later third being dedicated to actual .NET solutions. The other two thirds of the book has expanded my understanding of DI.Effective Java: A Programming Language GuideMany developers recommend this book even for non Java development. Many of the tips and advice included we not new to myself but the book provided further insight and explanation. A good summary of best practices for any OO developers.Web Application Security, A Beginner's GuideA good introductory book on security which provided a useful refresher for myself as well as providing a platform for further research on some of the key ar…

Reducing Conditional Logic to a Single Location

My Anti If Campaign post still generates a lot of questions and discussion. In summary:Conditional statements are not bad. They are a core programming construct.If you are working in an OO language, type checks are probably not the best solution to your problem. Rely on polymorphism.You need to perform conditional checks somewhere. In my first example the conditional check was pushed into routing. The conditional statement in this case had been offloaded to the consumer.A recent real world example was refactored which highlighted the points previously, but inverts the problem and solution. How do you remove conditional statements if your system itself has to make the decisions internally?ExampleThe simplified example shows the result of invoking a third party service. This result contained a flag indicating either success or failure. Effectively there were two hidden types here. Finally the result was returned based on the HTTP status code.After the result of the third party call, the…

ReactJS and JSHint

The ReactJS Getting Started Guide states that the recommended way of using React is combined with npm.This is great but poses a problem when trying to use JSHint. The default example outputs a single JS file containing both your code and the React library. The end result is the bundle when linted contains code you don't and shouldn't need to care about.The guide does provide a solution, though not as clear as it probably should be. Offline Transforms. These will transform your jsx files into plain Javascript without bundling react alongside.babel --presets react app.js --out-file main.jsSimply take the result of the transform and perform your linting process.jshint main.jsThis may seem obvious but I did lose some time realising the benefit of offline transforms.Offline transforms do require that you either bundle the transformed file with React, or you simply include the standalone JS scripts in your html. This can be done after the fact. JSHint can then play nicely with your …

Throw Code Away

The third and final part of my agile architecture series.Part 1 - You Cannot Iterate upon ArchitecturePart 2 - Don't Build a ThingPart one suggested walking skeletons for new features or projects. Part two suggested building the limited, smallest and simplest functionality possible. However you do not always have the luxury of deferral. Likewise if the project already exists and you are amending functionality, a walking skeleton is going to be limited.SolutionThrow code away. This sounds brutal and overkill, but throwing code away has many advantages.BenefitsThe second time around you will solve the problem quicker having benefited from first time. The first attempt is a prototype in this case. Throwing away prototypes is expected. They are not production ready, usually built with short cuts or quality comprises intentionally.The cleanest code is no code. Your following attempts will be cleaner. Knowing the issues from the previous attempt allows the ability to put code and proced…

Don't Build a Thing

Part two of my agile architecture series. Part 1 - You Cannot Iterate upon ArchitecturePart 3 - Throw Code AwayHere is a real life example of where I treated a unknown project incorrectly. Why I handled this is badly and how I should have handled it if I could rewind time.WhoopsAn external client had a proposal for a web service which would power part their new web application. This service sounded very simple. Data import and some basic querying. There were plans to add additional bells and whistles at a later date. After an initial meeting development began.A week later a second meeting was placed. A good few hours of development had been invested by this point. The meeting was useful, however some changes had cropped up. The data format had been modified, my solution could not handle the new format. Also the querying needed various modifications.A week later, after several more hours of changes, the second meeting landed. There were more changes. This time technical adjustments ba…

You Cannot Iterate upon Architecture

This is the first part of a series of posts as to why gradual iteration, doing the simplest thing that can possibly work over a software project fails in many cases. This series will explain why this is the case, and provide solutions.Part 2 - Don't Build a ThingPart 3 - Throw Code AwaySpotify has gave a talk on how it builds products and manages teams internally. This provides some great insights and advice. As part of this an incredibly effective image is used. This shows the production of a form of transport to travel from A to B.In the first half of the image, the product is built in iterations. Each step adds to the next. It is not until the fourth step that the product is able to take passengers from A to B. Agile development aims to solves the issues around this.The second half of the image is built iteratively. The goal is still the same. A product to travel from A to B. From the first version this goal is complete. However the team would be embarrassed to release in this …

Pre Computation

Caching is a common technique, especially with HTTP as it is made so easy. However pre computation is an alternative that can be used to reduce failures as well as speed up processing and response times.Caching ExampleAssume a list of countries to be displayed on the UI. These are often stored in one logical place, the database. A remote call is issued to query the database and return the results. The results are then manipulated and inserted into the UI. Repeat calls will then be cached for some period by the web server and/or proxy.Pre Computed ExampleAs part of the build process have the same query performed, dynamically building up the result set. Using a templating language modify a base source file which simply inserts the dynamic result set. The end result of this would be a source file containing a collection of countries as if you had hardcoded the values. The difference is these values are pulled from a single source of truth as part of the pre build step.In a statically co…

Coding In the Real World

As a student when confronted with a problem, I would end up coding it and thinking - how do the professionals do this?For some reason I had the impression that once I entered the industry I would find enlightenment. Discovering the one true way to write high quality, professional code.It turns out that code in industry is not too far removed from the code I was writing back when I knew very little.Code in the real world can be:messy or cleanhard or easy to understandsimple or complexeasy or hard to changeor any combination of the aboveVery rarely will you be confronted with a problem that is difficult. Most challenges typically are formed around individuals and processes, rather than day to day coding. Years later I finally have the answer. Code in the real world is not that much different to code we were all writing when we first started out.If I could offer myself some advice back in those early days it would be to follow KISS, YAGNI and DRY religiously. The rest will fall into plac…

Set Based Design

Each morning newspapers hit the newstands without fail. Live broadcasts are the same. Come show time they hit the air without fail. You can probably think of more examples of deadlines that are constantly achieved. So why does software development accept missed deadlines? Software development not only encourages software to be late, it has become accepted or just another risk to the project by default.SolutionImplementing Lean Software Development introduces the concept of Set Based Design (SBD). SBD provides an answer on how to never miss a deadline every again, providing the deadline is feasible. SBD will allow software to constantly hit deadlines just as newspapers and TV shows do.SBD requires multiple teams to implement the same functionality split over several sets (versions) of work. Each team works independently and in parallel to fulfil the same goal. This is in stark contrast to normal proceedings where each team is usually assigned to separate projects. At the end of the dea…

Header Interfaces vs Role Interfaces

In some languages such as C++ you must define header interfaces. These constructs define how a public type is exposed in terms of its public interface. Other languages take a different route. C# or Java do not require headers but they are still very much in circulation. This unfortunately brings along some unwanted side effects.Header InterfacesHeader interfaces are a one to one mapping of public methods that match the type it is defining. In other words, they are recreating the overhead of headers in languages that do not need them.Header interfaces tend to break the Interface Segregation Principle.Harder to switch objects via DI as you are forced to implement all members even if you do not use the whole interface.Prone to breakages as the one to one mapping means any change is breaking.Role InterfacesRole interfaces define the role an object plays. Due to various roles having different responsibilities they are usually grouped by functionality. Role interfaces are usually combined w…

Abstractions in Code, Details in Metadata

I've programmed many games - each one was special in its own way. One in particular stands out early in my university studies, a top down shooter. It was not graphics, gameplay, or sound that made it stand out however. It was the lesson it taught me about software development.Level OneWith the deadline for completion of the project looming, time was running out. The core game engine was complete but other than the first level there was nothing else for the player to do. With more marks awarded for various components I decided to add a second level.At the time the game consisted of a source file called level.cs. This contained parts of functionality explicit to every level that I would need. It also contained code specific to the first level. My solution was to extract a base class and introduce level.cs and level1.cs. This worked. The addition of level two was not as easy. The second level required a considerable amount of additional code, despite the shared functionality. A slow …

The Self Shunt - Test Doubles without a Framework

Generally you should favour hand crafted stubs without a framework by default. Before you reach for a framework there is another bridging step that you can take only pulling in a framework if complexity arises - the Self Shunt.Assume a simple Hello World subject under test where we can provide different formatters that format the message to a console, XML or JSON for example. How do we test that the formatter is used, with the right arguments?Enter the Self Shunt (pdf). Have the test fixture implement the interface aka assume the role of a message formatter. It provides itself as a parameter to the greeter in the form of self/this. The greeter uses this implementation during its execution, the test fixture can then assert or set state.BenefitsQuick and simple to get up and running.Most commands fall into the category of invoke something with some parameters, with little more complexity.Forces you to respect the Interface Segregation Principle, otherwise this technique can become painf…

Waste: Write Less Code

One of the biggest forms of waste is code. An estimated 80% of features in a software project are never or rarely used. This makes code the software development equivalent of inventory. Having a warehouse full of inventory is not a benefit, neither is having a repository full of code.How to Have Less Code?Delete it!As much as you can within reason of course, tests must pass and features must still work. Deleting feels great when you can successfully remove some legacy code. You'll be surprised at what can be removed. Commented out code, unused classes and methods are the obvious first candidates.Say No To Features by DefaultOnly add them if the benefit outweighs planning, designing, development, testing and maintenance costs combined. Even then, do you really need it? The advice here is do not listen to your customers regarding which features to add, instead listen to their problems.Libraries/FrameworksTry and see if a library or framework can handle your use case. They may not be…

Types of Test Doubles

Mock is an overloaded term in software development. Sadly this leads to developers answering with "mock it" when a mock object may not be the right solution. Test Doubles are a more general term. I should try to use this naming more than I do at present - a goal I aim to work towards. The result of choosing the wrong test double may seem innocent but the effect will be a very different style of test method, with increased coupling to implementation details. The following definitions are ordered in terms of complexity and increased coupling.StubsProvide canned responses. By their nature stubs would respond to queries. Stubs allow you to test paths of the code that would be otherwise difficult as they always provide the same answer.SpiesSimilar to a stub but with the addition that a spy records its actions. When responding to a query or a command the spy keeps track of what happened, how often and anything else relevant. The test can then inspect the spy for the answer, decidi…

Release It - Highlights Part 2

This is the second part of my collection of notes and snippets from Release It!Part 1 - Shared Resources, Responses, SLA, Databases and Circuit BreakersPart 2 - Caches, Testing, HTML, Pre-Computation and Logging (Future Post)CachesLow memory conditions are a threat to both stability and capacity.You need to ask whether the possible keys are infinite or finite and would the items ever need to change?The simplest cache clearing mechanism is time based.Improper use of caching is the major cause of memory leaks, which turn into horrors like daily server restarts.Your system should run for at least the typical deployment cycle. If you deploy once every two weeks, it should be able to run for at least two weeks without restart.Limit the amount of memory a cache can use.Caches that do not limit memory will eventually eat all memory.TestingEvery integration point should have a test harness that can be substituted.Make your test harness act like a hacker - try large payloads, invalid character…

Release It - Highlights Part 1

Release It! is one of the most useful books I've read. The advice and suggestions inside certainly change your perspective on how to write software. My key takeaway is that software should be cynical. Expect the worst, expect failures and put up boundaries. In the majority of cases these failures will be trigged by integration points with other systems, be it third parties or your own.My rough notes and snippets will be spread across the following two posts. There is much more to the book than this, including various examples of real life systems failing and how they should have handled the problem in the first place.Part 1 - Shared Resources, Responses, SLA, Databases and Circuit BreakersPart 2 - Caches, Testing, HTML, Pre-Computation and LoggingShared ResourcesShared Resources can jeopardize scalability.When a shared resource gets overloaded, it will become a bottleneck.If you provide the front end system, test what happens if the back end is slow/down. If you provide the back …

Production Code is Dirty

Production code is dirty. Dirty may be the wrong word however. Complex could be more suitable. Unlike code that is not yet in production, it is weathered, proven, and full of edge cases including numerous bug fixes. After some time this build up of additions can cause the code to be considered dirty or legacy.Greenfield development used to appeal so much more. Small classes. Small methods. Few dependencies. Just simple, clean code. Except this is not the case. Get into production and that clean code starts to weather. You'll handle edge cases, fix bugs and stabilize the functionality. That lovely, small, well factored application starts to accumulate dirt. The new code smell wears off and you're back waiting for the next new project so you can do it properly a second time around.This does not have to be the case however. Long living software such as operating systems, browsers and embedded systems are maintained and extended well after they were created. Production code can be…

Queue Centric Work Pattern

The Queue Centric Work Pattern (QCWP) is simple. Send a message declaring the intent of the command, acknowledge the message and proceed. All work takes place in a background process so the user is not kept waiting for the request to return. Acknowledgement usually takes the form of persistence to ensure that no messages are lost. Real life examples of the QCWP in action would be the sending of an email or the confirmation of an order being accepted from an online retailer.The QCWP will introduce the concept of eventual consistency, which surprisingly is not an issue in most cases. The queue itself should be implemented via some form of message queue that handles some of the more complicated technical issues regarding message meta data, routing, persistence and so on. Once a message queue has been chosen the code required to implement QCWP does not differ to far from simple request-response examples in terms of both complexity and lines of code.BenefitsReduced LatencyTransferring the …

Loops vs Functional Programming Styles

The following examples are four of the most common functional programming patterns that appear in mainstream languages though they may be known under different names.Being a fan of CQS and CQRS, queries work great when coding using the functional style. While this is completely subjective in terms of style there is another benefit - composition. In other words the functional styles below can all be joined together with minimal changes. A traditional loop would require additional modifications. The benefit composition provides is similar to the pipes and filter architecture - it is very easy to change the behaviour of the pipeline by simply adding or removing statements.Composition and concise code aside, traditional loops should not be avoided fully. Each scenario will have different solutions. Sometimes you really just want a standard loop.The benefit of learning the key concepts behind Map, Filter, ForEach and Reduce is the ability to translate these styles and idioms into other lan…

Why I Don't Like Mocking Frameworks

Disclaimer: By mocking framework I generalize anything that includes support for stubs and mock objects.The use of mocking frameworks was a difficult part of my TDD journey. Not only are newcomers expected to get their head around the basics of the practice there are now new tools to contend with. To make matters worse there is a lot of mocking frameworks out there with differing quality qualities and suitability.The use of mocking frameworks includes a variety of disadvantages.Readability suffers in most cases. You often find yourself asking what is exactly happening here? The frameworks themselves usually impose these constraints and make the issue worse.The use of frameworks tends to lead to header interfaces and not role interfaces being used. IDE's usually have a factor in this as they make this anti pattern so very easy to introduce.A lot of developers are not aware of what these frameworks are doing behind the scenes. This can lead to confusing tests and a general lack of u…

You Still Need Manual Tests

This blog has numerous examples of why unit, integration and contract testing is essential. However you still need manual tests. It is foolish to believe that all testing can be covered by automated tests despite the bias in this area.Why?Manual tests can catch anything you may have missed at an automation level.Manual tests can be unique. Use exploratory testing to try different scenarios.Manual tests that fail become automated tests, so they can never regress.Manual tests exercise the full stack. Many areas such as DI conventions, logging, and other framework related configuration are better suited to manual verification.UI changes require visual verification - automation is near impossible here.More Than Just FunctionalityOver the years, manual testing has caught numerous bugs, issues or things I've just plain missed. When you are deep in a problem it can be hard to see the wood for the tress. A second party manually testing provides an unbiased check of your code for a second …

The Benefits of Contract Testing

I previously claimed that you need some integrated tests but as few as possible. There are huge benefits to this approach, but there is a problem. How do you stop your test doubles falling out of line with the real implementations? The answer is to use Contract Tests.StepsCreate a base test fixture, this is where your tests live. All assertions belong here.Subclass this base class with each implementation.Override each setup step to provide the implementation that is to be tested.All tests should pass for each instance.ExampleIn this example there is a SQL repository and an in memory repository. It is not possible to change either in any manner that causes them to behave differently. We can safely use the in memory repository for tests, with confidence that the test double matches the contract of the real implementation.The test double implementations can be executed on every test run. While real implementations can be relegated to execution prior to commit or during continuous integr…

Integration Tests

It is well documented you need a balance between different categories of automated tests. The split is usually in the form.70% unit20% integration10% acceptanceWhile unit tests make up the majority of tests, there is a limit to their effectiveness. As soon as you leave the system boundary you need integration tests. Examples of when integration tests are required is code that interacts with databases, web services or the file system.These integration tests should not test logic, this is a mistake. They will become brittle and slow to execute otherwise. Instead of checking domain logic, test at a lower level. Go as low as you can without leaking implementation details of the subject under test. By going as low as possible you will radically reduce the number of integration tests required. Less tests means easier maintenance. Less tests also means faster tests.ExampleAssuming a SQL database, invoke the repository and test as lightly as possible. Do not indirectly test this repository by…

Static Code

Static code is considered a bad thing by developers. This is especially true when working with legacy code. The use of static code is often seen as a smell and should not be used.This is not as black and white as it first seems. Static code can be problematic when global state is involved. Not only is it hard to change, static code is very hard to test in an automated fashion. Bad examples of static code include persistence, third party services, and out of process calls. These examples should avoid static code where possible.One guideline that served me very well in my early days of TDD was treating static code as a death to testability. Unfortunately some developers don't move on from this guideline and treat any use of static code as bad.In fact static code can have a benefit. If a method within a class can be promoted to a public static method (PSM) it shows that the code is stateless. This allows the "extract class" refactoring to be performed. Without a PSM such re…

DRY vs Coupling in Production Code

Duplication in tests can be a good thing. The same can be said for production code as well in some cases. No. I'm not going mad. I probably wouldn't believe this if you showed me this several years ago either. More experience has shown me that loose coupling is often more desirable than removing duplication. In other words, the more duplication you remove, the more coupling you introduce inadvertently.Duplication of logic is bad and will always be the case. I am not debating this. You should only have one logical place for any domain logic. As always follow the DRY principle. However just because two pieces of code look the same, does not mean there is duplication.ExampleA system from my past had two places where an address was required for display and serialization. Billing and Delivery addresses.My gut reaction was to introduce a common address model that could be used for serialization and display. After all this screams of duplication. However a billing address and deliver…

FirstOrDefault in LINQ

Explicit null checking is a code smell in most cases. It should be limited where possible, or pushed to the edge of the system. A common anti pattern I've noticed is the incorrect use of First() in LINQ, which I have used myself on many occasions in this manner.Assuming a collection of items that you wish to query, the incorrect approach is to explicitly check for a null return value and act accordingly.The use of FirstOrDefault() is redundant because no default is actually set. The default value of a reference type would be null. Meaning the explicit null checked is required. We could use First() alone, but this will throw an exception if there are no elements to query against.A better solution is to set the default. As long as our initial query is not operating on a null reference this is safe. Here the explicit null check is gone. We have replaced it with a more functional solution which is after all what LINQ is based upon. While both are equivalent, the second example is much…

Do you really need a Microservice?

Lately there has been two sets of advice around the use of Microservices. Some advise that Microservices should be built after the fact. Others advise the opposite solution. In conjunction there is a third option that deserves more attention. Do you even need a Microservice at all? A recent tweet sparked off the exact thought I have found myself conveying.Creating a Microservice is no easy feat. Despite the limited code or functionality that is involved. There is a whole host of things that need consideration; source control, project setup, databases, project conventions, monitoring, logging, deployment, hosting and security to name a few.The so called monolith or "application" as it was known before is a tried and tested way of structuring applications. One of the big criticisms levelled against monolithic applications is coupling. Having worked with some terribly coupled applications I agree fully with this complaint, but there are steps you can take to prevent this.A whol…

Branch by Abstraction

Feature toggles are great for new features or features that are either enabled or disabled. Branch by Abstraction offers the same benefits as feature toggles but the seam to introduce the change is the abstraction itself. Unlike Feature Toggles, the use of Branch by Abstraction allows a gradual transition to new functionality.Start by duplicating the type or implementing a new version of the abstraction. The work in progress changes can be made safely while the system is using the original implementations. In order to demonstrate the new functionality, rely on automated tests or wire up the new version. Once fully integrated and tested, simply remove the old implementation. The addition or removal of implementations acts as the toggle in this case.To extend the SimpleReceiptWriter a new version is made. This work in progress implementation has no limit on the time to complete. The new implementation will only take effect once configured.Configuration takes the form of composition root…

Feature Toggles

I'm a fan of regular releasing. My background and experience leads me to release as regularly as possible. There are numerous benefits to regular releases; limited risk, slicker release processes and the ability to change as requirements evolve.The problem with this concept is how can you release when features are not functionally complete?SolutionIf there is still work in progress, one solution to allow frequent releases is to use feature toggles. Feature toggles are simple conditional statements that are either enabled or disabled based on some condition.This simple example shows a feature toggle for an "Edit User" feature. If the boolean condition is false, then we only show the "New User" feature and the "Admin" feature. This boolean value will be provided by various means, usually a configuration file. This means at certain points we can change this value in order to demonstrate the "Edit User" functionality. Our demo environment could …

Testing Private Code

A common problem many people ask is - should you test private code? In short, you shouldn't. You should always test the public api of your code where possible. This is not always easy. Based on the context of the code in question there are a few options available.Don't TestEither don't test the private code or rely on manual testing. This will not be ideal in many cases, but if the code is covered in higher level tests you may be able to get away with it. If the code will be stable, short lived or low risk you can default to this option.Test via Public TestsSimply test the private code by adding assertions or verifications to exisiting public behaviour tests. If the setup requires a lot of work, many edge cases or much duplication you may want to avoid this technique.Make the Code PublicOnce public, the code is easily testable. Are we making this code public just for the sake of an automated test? Yes, but there are valid times to do this. Providing the behaviour is logica…

Mob Programming

I first saw this video of Mob Programming a couple of years back. Mob Programming is pair programming taken one step further, the whole team is based around a single machine. The developers rotate regularly and those who are not driving can add feedback, make suggestions or simply watch and learn. Everyone should be placed on a level playing field. I will admit to being highly sceptical of Mob Programming at first.I advocate walking skeletons to ensure we are on the "right path" when developing. We wanted to do these as a team, during our planning and tasking phase. I suggested mobbing rather than watching a solo developer on a projector and it turned out to be quite fun. We also learned a few new tricks such as keyboard shortcuts or IDE techniques along the way.There were a few rough edges, mainly due to the setup used. A laptop around a screen proved difficult and this in turned seemed to put pressure on individuals. In repeat sessions we have used a dedicated space, with …

Walking Skeleton

A Walking Skeleton is the thinnest possible slice of new functionality that can be delivered end to end. The term "walking" refers to the ability for the feature to "stand on its own". You should be able to deploy a Walking Skeleton and demonstrate it. Just like a human skeleton is an incomplete body, a Walking Skeleton is an incomplete piece of software with many internals stubbed, not implemented or consisting of basic functionality.While the software won't do much it provides rapid feedback. It allows your build and deploy pipeline to be set up if not already in place. More importantly it gives developers a framework or scaffold to work with.Production of a Walking Skeleton should be fast. Components such as which objects to introduce should ideally be developed top down, however the actual direction each solution takes will vary. Some design will still be required, but the choice of patterns or implementation details should be deferred where possible. Core …

Tasking in Software Development

Tasking is core part of XP, Kanban, Scrum and other software development methodologies. It is required when more than one developer is working on a feature. I consider it to be the most wasteful part of the development process as practiced in the mainstream.Tasking typically involves the team sitting around a machine/desk/whiteboard/projector. From past experience this can take anywhere from an hour up to a day or more. Engagement is often low and this process can be both mentally and physically tiring. During which many assumptions about what should be done is made.The end result is nothing but index cards, scribbled diagrams or other lightweight documentation. These artifacts are often transformed into digital versions.ProblemsThe foolish understanding is that now any developer can pick up a task and start work. This leads to dependent tasks being worked on in an independent manner. Team members then find themselves being impeded until a certain piece of code is in place. No amount …

Getters and Setters are Evil - Redux

Back in early 2011 I wrote one of my most viewed and commented posts at the time - Getters and Setters are Evil. Four years later it's time to review this.The feedback within the team was generally positive. Production code was written in this style to great success. The core benefit was encapsulation was preserved as Business Objects were the sole source of domain logic. As an additional side effect testing was easier.However not everyone within the team agreed that the benefits were worth the extra hassle or believed in the benefits of encapsulation. I always found the addition of an IRender interface or similar broke the SRP, even if you moved the logic to a separate class. The OCP suffered too, if view requirements changed, you need dig out your business object. The biggest failing is that legacy code and frameworks still require public getters/setters to function.Overtime I found myself and others slipping back to the "old ways" of applying getters/setters without t…