Wednesday, 4 October 2017

Concerning Seperation


The majority of good engineering and architectural practices in software development can be traced back to the principle of separation of concerns.

A concern may be deemed a reason to change, a functional unit or a set of connected pieces of information but whatever the definition it should be possible to demonstrate clear and distinct separation when it comes to the implementation of particular tasks and actions in a code base.

When this isn't the case we instead see the emergence of spaghetti code, instead of well formed modules of code we are left with interwoven and potentially unintelligible software that is very difficult to pick apart.

It should be possible when explaining the construction of a code base to identify a certain layering along the lines of the concerns that will broadly fulfil common objectives.

Business Logic

Software generally exists to represent a particular business domain, these domains will always have particular rules of operation and practices that define what it is the business does for its users.

This logic when implemented in a code base should be distinct from the implementation of integrations, such as REST APIs, that provide input and also to the mechanism that is used to present the output to the user.

The business logic should be unconcerned with how the data it is using to make decisions is being provided and also to what will be done with the outcomes it is producing, it is purely about logic.

Model

Alongside logic a business domain will also likely have a representation of the data it deals with and how it models its world.

These model classes should be only concerned with representing these entities and be entirely dumb.

It should be very rare to see logical programming constructs in these model classes as the place for logic and model manipulation is in the business logic that represents the rules that govern the management of the model.

A simple example might be that a class representing a bank loan shouldn't contain code to calculate repayment values or fines for late payment, it should simply represent the amount owed by the customer.

Services

No piece of software is self contained, it generally relies on the flow of information in and out of its own sphere of operation.

These flows might be API calls to servers, to functionality within the underlying OS or to other pieces of software deployed alongside it.

All of these interactions should be encapsulated within a service layer, the responsibility of which is to understand the mechanism for the interaction and how to either retrieve or send the relevant information.

A service layer interacts with outside entities so you don't have to, abstracting the detail so other parts of the code base can concentrate on what can be achieved with the information being provided.

These services should also be small enough to make possible the opportunity they can be composed to perform more than the sum of the parts. 

Presentation

Most software at some point must open up a window into its world, both showing the user the current state of its domain whilst also accepting input from the user to change that state.

Any logic in this layer should be solely concerned with choices related to the presentation of data, at no time should it be deciding what data should be displayed only how it should be rendered.

Being the only area of code the user can interact with means it is necessary the presentation layer be the first to be informed of a users input, the code in this area should be the minimum to provide a linkage between this input and the business logic that will decide the outcome, not filtered, second guessed or pre-empted.

Most reasonably complicated software will have more distinct layers than have been presented here.

These might cover hardware abstraction, platform abstraction or any other distinct functional units that can be identified.

Whatever they may be it should be possible to draw a ring around them on an architecture diagram rather than a many sided polygon.

A single concern is a single reason to change and a single reason to cause a bug.

The impact this will have on the maintainability and extensibility of a code base is immeasurable and is pretty much the only way to achieve a clean architecture.

The challenges are firstly to clearly identify these concerns and secondly to have the discipline to resist the temptation to mix them.

As with many good software development practices a healthy dose of pedantry and an obsession with order can go a long way to providing good outcomes.       

Monday, 18 September 2017

Ruthless Engineering


It can be easy for teams to end up being hampered by two sources of distraction.

One can be an over-emphasis on solving problems that don't yet exist, this might be termed over-engineering or simply unwarranted future proofing.

The other can be becoming to wrapped up in the coolness of what is being built or the softer elements of how customers will interact with the software being produced.

While its a delicate balancing act, as effort in all of these areas can be valuable, a healthy amount of ruthlessness should be demonstrated in trying to achieve our goals.

Ticket to the Game

The goal of any development team should be to ship software, not only is it demoralising for the fruit of a teams efforts to not be delivered to users but an organisation gains little or no value when software is written but not given to users.

Shipping software is buying a ticket to the game, trying to perfect your offering to users while admirable will often lead to others delivering what maybe a sub-optimal experience but that gets them into the minds of users.

This shouldn't be taken as a remit to ship software without it being properly engineered or without due diligence around its proper operation.

It is a call to remove all obstacles to the delivery of software into production, sometimes these are technical and sometimes process driven, but a ruthless pursuit to reduce the amount of time between coding being completed and code being shipped should be the goal of an effective operation.    

Working the Numbers

Once our software has shipped then we should immediately turn our attention to determining if we have built the right thing.

As much as we want to please our users garnering their feedback can be an imprecise science and we may have additional goals for the code we shipped that we also need to evaluate.

Any data we collect towards this goal has to be actionable, we must be able to define metrics we can accurately measure and where a plan of action can be defined for whatever direction those numbers may move in.

We may feel that some of the value we provide towards to users is intangible or immeasurable, while that may be true effective product development must be data driven and progress must be demonstrable.      

Change of Direction

When we evaluate our carefully cultivated data we shouldn't just be trying to prove our original hypothesis correct, we should be prepared and almost expectant that it shows we got it wrong.

One of benefits of an agile is approach is the ability to pivot when we are proven to have taken a misstep.

To have an agile mindset is to accept uncertainty and in accepting uncertainty were acknowledging the possibility we may get things wrong.

Achieving this ability to pivot is a combination of flexible engineering, constructing a well segmented and organised code base that can be changed without re-writing, and a business mindset to not create plans built on an assumption of always making the right call.

Ultimately engineering must have a purpose the production of software is not an end in and of its self. It is a tool we use to achieve outcomes, both for our users and to reach our own goals. 

This purpose can be re-enforced by application of a scientific method, creating an hypothesis, devising an experiment and looking at what the data tells us.

Sometimes we will be right and sometimes we will be wrong, but while we remain in the game we can look to improve our numbers and roll the dice again.   

Monday, 4 September 2017

Mobility Issues



Each branch or flavour of software engineering has its own quirks and nuances, whilst the fundamentals of writing good software are fairly universal, experience with a given platform or use case tends to develop over time to adapt these truths to the situation.

Mobile development is no different, many years ago this would have been characterised by a lack of resource, whether that be CPU, memory or data storage, however those concerns have all but disappeared with the advent of powerful smart phones.

But none the less mobile development still has some unique challenges, while sometimes this can make an engineers life difficult its also what can make the process more interesting.

Diversity Across the Spectrum

Web development can be complicated by the various different browsers that users may be using to access a site, standards for web technologies help smooth this variation but it still exists.

Mobile development is complicated by much more variation in multiple factors, whether it be software, hardware, screen size, screen resolution, available peripherals or age of device.

All of these factors can vary independently leading to the number of variations an app could be expected to be compatible with to be measured in the hundreds or possibly even thousands.

The counter to this is to be to identify where all this variation could impact your codes operation and ensure adequate abstractions are in place to enable you to craft some conformity for important pieces of functionality.

Its also important to be realistic about which elements of variation cannot be controlled and whether or not it is realistic to try and target all the platforms all the time.

This may lead to a progressive approach where the more functionality a platform or device is able to offer the richer the app experience will be.

Lack of Connectivity

Connectivity to APIs and services has become an essential part of most code bases, very few pieces of software are written in or run in isolation.

When developing server side applications the infrastructure to provide this connectivity is virtually guaranteed, outages may happen but shouldn't be a regular occurrence.

Code that runs on a users personal mobile device cannot rely on this robust connectivity.

Environmental constraints related to where users happen to be trying to use your code can cripple an application if no strategy is in place for when API calls cannot be made.

It is very difficult for a complete lack of connectivity to cause no issues but whenever a flow of execution involves a service call consideration should be given to what happens when that call cannot be made.

This maybe be implementing a retry strategy, limiting the required number of round trips to the server or pulling down data ahead of when it will be required.

Window of Opportunity

The nature of how people use mobile devices means they rarely use your software with an anticipation of spending a large amount of time interacting with it.

From the moment they launch your app you have a limited timespan to provide the functionality the user wants and to draw their eye to the areas that you want them to notice and interact with.

Uses also utilise their devices for many different purposes so you have to deliver your functionality whilst competing for the users attention when they are presented with many other distractions and apps trying to fulfil similar needs.

Context, Context, Context

The fact mobile applications run on a device the user has with them pretty much at all times and when on the move means a wealth of additional context is available for your app to leverage.

This may be where the user is, what they are doing, what they are trying to find, where they are trying to get to.

If used properly this enables your app to make itself extremely relevant to the users needs and wants.

It can also enable your app to figure out over time how it can become more relevant to the user mining all this addtional context that not all code is able to take advantage of.

In any area of development the unique challenges it present should be embraced as opportunities to make a difference, this is where you can differentiate yourself from your competition and make yourself more useful.

Always keep in mind the universal truths of all development but learn how to apply them to the field you are working in, while we may agree on the structure of the cloth, one size does not fit all.

Monday, 21 August 2017

Everything as a Service


As the benefits of cloud computing have started to become fully realised we have seen the birth of the As-a-Service model.

Software as a Service (Saas), Platform as a Service (Pass), Infrastructure as a Service (IaaS).

This combined with the birth of the DevOps mentality has lead to a shift in how we approach the provision of the services and functionality that we always require on any new project.

While each variant of this approach has differences in the degree to which you are involved in the detail all carry similar benefits compared to the traditional do-it-yourself attitude.

Details as a Service

While many of us may take enormous pride in managing and maintaining boxes and servers ultimately the amount of effort required to do this is often out of the proportion with the value derived.

Many of us will also have spent many frustrating hours dealing with an issue related to a Java install, a file system problem or a connectivity issue. We aren't in the business of maintaining servers but it is a necessary evil to have a functioning software delivery pipeline.

As-a-Service enables us to abstract ourselves from this necessary yet effort draining detail and focus instead on the business of delivering value to our customers via deploying software to these servers.

By taking advantage of an army of people dedicated to keeping our servers up and running we gain reliability while reducing to almost zero our effort on these day to day activities.

Expertise as a Service

Building and deploying software involves many different tasks, each with there own skillsets, as we become more experienced we will gain a certain level of skill in all these areas but it is unlikely we could ever call ourselves experts in all of them, we are also unlikely to be able to dedicate enough time to achieving that level of proficiency across the board.

We therefore do the best we can.

The functionality delivered via As-a-Service is backed by experts in their field, people who have amassed expert knowledge in the service they are offering that it would be impossible or at least impractical for us to attain.

This engineers a situation where we are essentially filling our team with experts in every aspect of delivery amounting to years and years of experience being added to our team in specific technologies, domains and functionalities.

Compared to this there is no benefit in developing these systems in house, while it may be fun we cannot hope to improve on what is delivered by experts employed to achieve excellence.

Availability as a Service

When we construct infrastructure for use in deploying and hosting our code there is generally an on-going cost to its availability, maintenance and provisioning.

This cost is usually not in line with our utilisation of the infrastructure, although it will rapidly increase whenever that infrastructure is down for what ever reason.

As-a-Service in most cases is like any other product or service you may buy with it being charged based on your usage.

This can obviously lead to cost savings for the services we need to be available but only use infrequently, but the other consequence is that we have a virtually unlimited supply of services where are need scales with the amount of delivery we are trying to achieve.

As an example, when provisioning our own build system we are likely to be faced with a choice of imposing a cap on the amount of throughput our developers can achieve or hugely over provisioning at great cost.

An As-a-Service build system can start off small but via some tweaks to settings and a proportional increase in cost can be made to achieve more. As developers get into a groove the build system can be made to adapt and ensure every last ounce of effort can be converted into the production of code.

Within software engineering we have a healthy track record of laziness, trying to use our skills and abilities to make our work lives easier.

As-a-Service is the ultimate manifestation of this attitude with the goal of ensuring developers are almost entirely focused on writing code.

While we may be capable of doing or learning to do all these tasks the question you have to ask yourself is would that add any value.

The emergence of several major cloud providers and the competition starting to grow between them means the answer to that is almost certainly no.    

Monday, 7 August 2017

Assurance of Quality


Its a truth many software engineers have a difficult time accepting, but all software no matter who produces it, usually has bugs, defects or doesn't quite meet requirements.

Once that fact of development has been acknowledged then the need to have some level of quality assurance is undeniable.

The traditional model encourages a left to right approach where coding is completed, code is tested, code is shipped.

This "throw it over the fence" mentality is inefficient and thankfully we have developed modern techniques to ensure quality isn't an after thought.

Shift Left and Automate

The ideal situation to be in is that defects never make it into a code base, they are if you pardon the pun stopped at source.

Realistically this will never be the case, but if we can move much of our quality assurance nearer to the point that the code is written we will realise huge efficiencies in producing effective working code.

This can be achieved in two ways, by introducing more automation and by having a slick and quick deployment pipeline.

Unit testing, functional testing, integration testing, we have many techniques at our disposal to very quickly verify if changes to our code base have had a negative impact in its effectiveness to perform its intended task.

By ensuring our code can be quickly deployed to the environment it is designed to operate in we can create short feedback loops to identify and address any regressions.

Whether this be by quickly making new versions of code available to a test team or by employing UI automation to validate the continued operation of our key journeys, the quicker issues can be identified the quicker they can be fixed.

Break Things

The advantage of automating and streamlining the testing of the routine is that it releases the valuable resource of test teams to focus on more abstract testing of our system.

The main purpose of this testing should be to break and destroy.

By taking the use of our system to extremes we can uncover inefficiencies or flaws in logic that otherwise would be difficult to find.

A common problem when testing software is the relatively limited spectrum of use we are exposing it to. Often a defect that we may think of as relatively obscure may actually be produced hundreds or thousands of times once its out in the field being used by a large user base.

By actively trying to break things we focusing our attention on the problems and issues that are most likely to affect people.

This destructive approach may take may forms but the important thing is not to concentrate on validation of correct operation but instead employ a machiavellian approach to try and cause chaos.

Test in the Field

Even when we implement all the methods and techniques available to us to enforce quality some problems are still going to make it through the net into production.

Because of this unescapable fact it is important we have an effective strategy to properly instrument our application to report back valuable diagnostic information, both when things are working and when there not.

Whilst it is not a sin to ship a defect, it is at least negligent to not have the ability to detect the effect on users.

Our software should be like a machine we are constantly monitoring and inspecting, whether it be out and out bugs or simply measuring a decline in performance it is vital to pay attention to what is going on out there in the field.

As well as creating a feedback loop to improve your system, analysing how people are using your software will also provide great insight into the direction it should be going in.

As we progress as an industry its important we develop and improve all aspects of software development and that includes how we test and validate the code we produce.

Innovation in this area will bring great rewards and help you stand out from the crowd.

Accept that defects and bugs are a fact of life and develop a strategy to seek and destroy as many as possible, also realise that its everyones responsibility to play a part in achieving this as we all bring different skills to the table.

Sunday, 23 July 2017

The Forgotten Features



Those of us that work in the technology industry can very easily demonstrate the traits of magpies, becoming distracted by the new and shiny, always wanting to break new ground and be first.

Sometimes this can be at the expense of taking care of what may appear to be the mundane but what it is actually the life blood that is paying the bills.

This is not an argument against innovation, it is instead an acknowledgement that the right to embark on such escapades must be earned by making your core offering bullet proof.

To forget about these features will taint any advances you may make in other areas with the anger and frustration of your users.

Performance

Users are generally very impatient people, the move towards a more mobile world has only increased this tendency.

While it may seem obvious that your site or app should be performant, our excitement at the unveiling of our latest and greatest sometimes blinds us to the fact that the users patience is unaffected by this anticipation.

Set minimum requirements around the performance of certain key aspects of your system, this should especially concentrate on aspects affecting access, the time taken to login or the time taken to checkout.

Also don't become to complacent when your code runs like a rocket during development when it only has to service you as a single user, a lack of performance at scale is unfortunately all to common.

Available at Scale

Even the coolest of new features carries very little impact when users can't access it.

Scale is a consequence of success, to not have an effective strategy for scaleability is to have no plan on how to deal with success.

Scale also costs, even in a cloud computing world throwing more tin at the problem will become very expensive very quickly.

We have never had so many tools for dealing with scale, creating an elastic infrastructure to have multiple nines of up time is within everyones reach. Being down should be considered a sin with the penance being disgruntled users who may never come back into the fold.

Pay attention to errors and understand the workload your system is under by unwavering analysis of how and when your users are most active.

Deployability

If you question your users on what features they would like to see its unlikely any of them would mention deployability.

They may not know it but they want this.

If software isn't easily deployable then it is slow and ineffective at delivering value, no user benefits from code being in source control, effective continuous integration is the mechanism for implementing continuous delivery of value.

This leads to continuous bug fixes, continuous security patches and new features being available the instant they are ready.

Being continuous isn't about cadence no matter how regularly you deploy, its about there being no barriers to deployment and having the ability to deploy whenever you want to.

Even though they may not have asked for it your users will thank you for it.

If you ask users if they would like to see this or that feature they will invariably say yes, don't take this to mean that they want your system to resemble a Swiss army knife.

If you ask them what is bugging them about using your site or app, some may mention a potential new feature but most will talk about problems that can be linked back to performance, scaleability or availability.

Put simply, users aren't asking for the world they just want things to work. Satisfy this ambition by concentrating on what your core offering to them is and make your system the most effective delivery mechanism for that offering the world has ever seen.

Once your able to make this claim the time for innovation will come but never at the expense of any of these to often forgotten features.

Sunday, 9 July 2017

The Modern Age


All industries can point to different eras as practices, techniques and perceived wisdom undergo constant evolution.

Many would point to technological industries as taking this to extremes with the pace of change often deemed to be frightening.

While I think this aspect of the industry is often over exaggerated it is undeniable for such a comparatively young discipline software engineering has evolved and changed many times.

If this is true then what defines this modern era of software development?

Continually Under Attack

There has been a proliferation of the use of technology such that it influences every part of modern life, along with this ever increasing use of technology has come an ever greater understanding of how to harness it.

Never have so many people known enough to be dangerous.

Whilst IT security has always been a concern its importance in any modern day system is now such that it is negligent in the extreme for it not to be given focus.

Expose any piece of technology to the internet and you may be shocked at the speed at which it will start to be probed and investigated. These attempts won't always be malicious but many will be looking for weaknesses and vulnerabilities that may lead to a bounty.

An entire industry now exists solely to facilitate these attacks, newly found exploits being shared in kit form to allow anyone with even the most minimal computing skills to be a potential threat.

In the modern day it pays to be paranoid, everyone is out to get you.

Cloudy Days Ahead

The workhorses of an internet driven world are servers, everything can eventually be traced back to these pieces of tin.

An appreciation for the building and maintenance of these boxes used to be a primary skill for someone wishing to deploy software to them.

The advent of the cloud has removed that requirement, in the modern day only suckers build servers.

The arrival of the cloud should lead us all to a mindset that its a waste of a valuable and talented resource to have engineers working on already solved problems.

By using the services offered by the cloud, whether this be PaaS, SaaS or IaaS, we can ensure we concentrate on the areas where we can add value. How much value are we really able to add to building infrastructure others have already mastered?

The days of developers having to worry about problems with the JVM, disk IO or registry settings should be very much over.

Although our industry is young we should look to take advantages of the areas of maturity we do have and not think our wheel can be better than the ones already invented.

Always be Deploying

Traditionally we operated in a world of releases cycles, set points in time when we released software.

This cadence brought with it a gradual rise in tension culminating in a stressful pushing of the button to deploy to production.

Thankfully we have learnt from these scars and developed technology to ease the process of delivery, this combined with the emergence of the cloud has made our release strategy must simpler, in the modern world when software is ready we ship it.

The drive to develop this technology wasn't a war on complexity it was a war on fear.

Fear comes from uncertainty and what we have come to realise is human processes foster that uncertainty. The monotony of automation applying unrelenting consistency takes away that fear and allows us to manoeuvre ourselves to a position where deployment is a consequence of work being completed not a conscious and fearful action.

Someone may well look back on this post in years to come and think it quaint thinking I'm describing advances that themselves have been superseded. But hopefully we are still following a strategy that the best way to deliver technology is with technology.

Problems and threats may change and we have to adapt our practices to suit but ultimately our core skill set is technological, we are experts in it and therefore it is probably the root to a solution to our current set of problems.                

Friday, 9 June 2017

Do More with Less

Software engineering as an industry and a discipline has developed an unfortunate tendency to repeat a flawed approach to scaling the delivery of value.
An initial small team of engineers develops something that proves popular and has great potential, this leads to a hunger to build on top of this success. Unfortunately this hunger is very often not paired with patience and we fall back on a view that software production can be made to obey the same rules as any other production activity.
More people will produce more code, I believe a fundamental misunderstanding of that mantra is that value delivered to customers doesn't scale in proportion to the amount of code written.
Too Many Artists
Software development is very often more aligned to an art then a science than people may realise.
Whilst it is true to say more developers will produce more code, or at least will produce more change in a code base, it is not true to say that this means the value being delivered must be increasing.
If this were true software development would be a solved problem, it would be possible to fully automate, and bugs and defects would be a thing of the past. Anyone that has worked in software development will know that is not a situation that is likely to arise anytime soon.
If instead we recognise that there is a healthy dose of artistry in developing good software then we find ourselves contemplating if multiple artists can speed up the time to produce an art work.
The answer may be yes but do we think the art work produced would be a pleasure to look upon or more of a confused and incoherent mess.
Frequency over Speed
A strong drive for scaling out software development is to increase perceived speed, unfortunately this very often focuses too much on the speed of development as opposed to the speed of delivery.
Software development can be a tricky business with a multitude of unforeseen circumstances and potential pitfalls, creating consistent and speedy timescales is far from easy.
The delivery of software into production can be made a much more precise science, however quite often not enough effort is put into conquering this more easily understood challenge.
Putting effort into producing a well defined and smooth delivery pipeline provides a mechanism to ship software whenever it is "ready".
During the good times this maybe several times a week or even daily, what it ensures is that the effort of software developers is being maximised by ensuring there is very little delay between them producing value and that value being shipped to users.
This will go a long way towards creating a feeling of speed and effectiveness even if the software development process has its normal bumps in the road.
Architect for Change
For all these good intentions around how to manage software delivery there is also a technical aspect that must be in place to make this approach viable.
None of these good practices can be put into place if the code base involved is a monolithic beast.
The only way to create a situation to increase the number of developers working on a code base is to put in place the loose coupling and good organisation that defines clear independent segments of code for a team to work on.
It must be possible for these teams to both work independently but also to deploy independently, the moment large dependencies start to exist between these teams the more they will grind to a halt.
This means an architecture must be in place that describes well defined, de-coupled building blocks whilst also providing a vision of what will be possible when these blocks are grouped together.
This amounts to increasing the delivery of software by increasing the number of distinct blocks of software that can be delivered.
It is a source of enormous frustration to many that the lessons of what does and doesn't work when trying to scale software development have failed to be learnt almost since the industry's inception, this can lead to a feeling that we are doomed to never fully realise out potential.
One of the potential reasons we haven't taken on board these learnings is that it requires us to trust in the skill and professionalism of our development teams, it requires that we hire the best people and allow their talent to deliver.
This can lead to a feeling of inadequate control over outcomes but if we hold our nerve and trust our teams we will find they will deliver everything we need, the control we think we gain by adding more cooks is an illusion, eventually the broth is spoilt.

Monday, 29 May 2017

Later Never Comes


Within software development there are many different variations on the theme of putting off till tomorrow something that could be addressed today.
We'll fix this later, we'll sort this out in a future release, we'll re-visit this again at some point.
The main problem with most of these phrases is that later often never comes, to put it another way the problem with throw code is we very rarely throw it away.
There are good and bad ways of being pragmatic in balancing the need for engineering excellence and the need to ship software. Whenever the former is placed above the latter we call that technical debt and we must all be aware of its implications.
False Velocity
The first impact of technical debt is the unrealistic expectations it gives birth to because of the misleading uplift it provides in a teams velocity.
Increases in velocity brought about by deliberate tech debt have essentially been bought on credit with the debt being collected by a future downward swing in velocity or in bugs and defects being shipped to production.
Unfortunately we very often limit our assessment of the health of a project to the short term and only see momentary uplift the tech debt is providing, when the debt comes back to make its presence felt we attribute this to variety of other explanations and don't connect the dots back to the decisions taken in the past.
We need to realise that the only changes in velocity that are sustainable are the long term trends that we gain by addressing how a team is operating not in deliberately weakening our product.
Refactor Ratchet
Refactoring is a core skill of software engineering, take any sufficiently complex technological system and you will see a continual cycle of build, learn and refine.
Too often people believe refactoring is a synonym for re-writing, this tends to either lead to a belief that it is required because of mistakes in development or simply because of a desire by the developers to do something more interesting.
Building software is a complex business, it is near impossible to nail a design or architecture on the first attempt. As more information is gained around what the system is growing into and what it needs to achieve it will become necessary to re-evaluate the approach and refine.
One of the first casualties when we decide to fix things later is unit testing, this severely impacts a teams ability to effectively refactor code. Each attempt to refactor becomes a leap into the unknown without the ability to quickly verify continued correct operation.
It is usually the case that required refactoring grows in line with the amount of untestable code in a code base, without a design for testability strong coupling can mean any attempt to address issues in the code will involve a large area of affected code.
Software Isn't Magic
With software we can sometimes do magical things but the engineering that gets us there is far from magic.
Although we work with a different raw material we are subject to many of the same rules and truths of any other form of engineering, build you house on weak foundations and it will eventually collapse.
It is very easy for the efforts and hard work of a development team to create a false sense of security for stakeholders that no matter what decisions are made the product continues to work even if the team are warning about future possible calamities.
In many other forms of engineering such an approach would be considered negligent, if warnings about the structural integrity of a bridge or building were ignored we would rightly be concerned.
Although cracks in a software product are not always as visible they are there if long standing tech debt isn't addressed, similar consequences can also be expected.
Its unrealistic to say we will never incur any tech debt, sometimes in the interest of pragmatism it is necessary to do just enough engineering.
Most developers will accept this, the cynicism and skepticism when told we will fix this later is born from the fact that they have become used to later never arriving because new features are on the horizon.
Software systems are like any other system, ignore its deterioration and it will eventually fail, if you have a good development team they will often find a way to moves this date back but all this often achieves is increase the size of the eventual breakdown, eventually quality isn't optional.

Monday, 22 May 2017

Homework Excuses


The majority of developers will recognise the benefits of unit testing and most will make an effort to write some.
But often the determination to ensure high test coverage can be paper thin with excuses often being made for why in this instance we won't write tests or fix the ones we've broken.
On a large number of occasions the root of these excuses will highlight something more fundamentally wrong with a code base.
False Assurance
Sometimes a justification for not writing unit tests right now is that "I know this works".
Firstly, I'm sure all developers can recall a time when their certainty in something being true has turned out to be false, this would come under the same category as "I haven't changed anything" or "That can't happen".
Unit tests not only prove a class does its job but it also ensures that it is achieving this outcome in a way thats expected.
Secondly, and probably more importantly, the value of unit tests isn't really in proving that code works at the time of writing. As professional developers this should be a given, pull requests shouldn't be raised for non-functional code.
The real value of unit tests is proving code works at any and all points in the future. The longer you leave code uncovered by unit tests the wider the window that a bug or regression will be introduced unseen by the team and possibly making it into production.
A healthy if slightly distrustful attitude in this situation would be "you say this works, show me the tests".
This Is Hard To Test
Another common excuse is that this particular class is hard to test.
Sometime this can be true for a variety of reasons, but a healthy attitude to this situation is to assume that this must mean some refactoring is required, with code only being declared untestable when a clear rationale has been defined.
This should largely only be the case when the code that requires testing has a dependency on something we don't have the ability to change or that cannot easily be mocked.
Un-testability is something that can spread through a code base like a virus. An untestable class spreads the disease to every class it becomes a dependency too. The result of this is that the scale of required refactoring increasing rather than our code coverage.
So whenever someone declares a class untestable assume that this is our fault until proven otherwise.
Much More Than Testing
While the major benefit of unit testing will always be testing our code it does bring other benefits that shouldn't be dismissed.
I am a big believer that unit test make the best code documentation. On many occasions when wanting to understand more about how a class is supposed to be used I have looked through the unit tests, when these tests are well written I find a plethora of information about how a class is interacted with, what error conditions can occur and what output I should expect to get from it.
Unit testing is also a key part of building a continuous integration and deployment workflow.
Whenever we talk about CI we are not referring to a pipeline that continually delivers code of a questionable quality with a varying probability of working.
If we intend to automate the delivery of our software then we must create an environment where we can automate some degree of certainty that things are working.
This will never be full-proof, even the most highly tested code has been known to contain bugs, but I would back it over a fingers crossed approach.
Whenever code is deemed to be untestable we need to view this as a design decision because it comes with consequences.
This attitude needs to be pragmatic as nearly all code bases will have areas that genuinely aren't practical to unit test, but this should be a last resort with a clear rationale not an immediate decision made every day.