5

We have an application where the customer has changed their mind about a large area of functionality and this area requires a large amount of re-work.

Whilst the re-work itself is not an issue, it has introduced a situation where the impact of the changes is wide ranging including:

  • Data
  • Query Logic / Table structure
  • Business logic
  • Display / Presentation

Ideally we'd like to incorporate these changes at the same time as several other development streams, but we're worried that the impact of the changes will have a negative impact on how frequently we can deliver. We can cater for some of this through branches, but it made me wonder what the possibilities are for guarding against this kind of thing from an application architecture or development practice point of view.

Things we currently do:

  • Abstract query logic into repositories - This is great for small changes, but when the overall logic in an area changes, e.g. instead of getting a list of users which meet a certain criteria, we now need to get a list of organisations which joined to locations.
  • Abstract business logic into services - This is exactly the same as the repositories in that we can't really guard against entire logic shifts, but small changes can be accommodated with limited impact.
  • Ensure configuration is kept out of code which needs re-deployment, a combination of database rows or web.config values.
  • Other patterns which promote aspects of flexibility like factories where appropriate etc.
  • Feature flagging for selection of functionality if two approaches were developed originally. etc

Are there any other current best practices to guard against changes, without incorporating branches etc?

5
  • possible duplicate of What characteristics or features make code maintainable?
    – gnat
    Jan 21, 2015 at 14:15
  • Yeah I guess that could be true, I think I was thinking that the code itself 'is' (to an extent) maintainable, but are there any ways of managing two parallel versions of code, without impacting on the ability to deploy. One option would be to develop a second version alongside, e.g. via branches. Whereas another option may be to duplicate the code and feature flag one, until the second is complete. Jan 21, 2015 at 14:24
  • 1
    @dougajmcdonald it's unclear to me what you're asking. Does the problem lie in the fact that other developers working on unrelated stuff are hampered by these breaking changes ? Or that it takes time to get big reworks right (which there's honestly not a lot you can do about...) ? Jan 21, 2015 at 14:58
  • I would argue to remove the C# tag. This type of thing is applicable to any language capable of building a large client application like this.
    – Neil
    Jan 22, 2015 at 8:46
  • Some things that come to my mind, which, while they probably won't be relevant directly, might be handy to have in mind as you go along, are: branching by abstraction and Hexagonal architectures
    – paul
    Jan 22, 2015 at 20:47

2 Answers 2

4

The only thing you can do to help reduce the impact of a change is to split your overall project up into many components, so while a major change will impact several of them, many will not be affected.

eg if the customer decides they need a new button that sends data through the middletier to be stored in a new column in the DB, you're going to have to change every layer in your system. However, if the system is also split 'horizontally' too then the UI impact will only affect part of the UI layer, the rest will not be changed. Similarly, if you have a lot of services in your middle tier that each process certain parts of the data, you'll only have to change one of them - the rest will be unaffected. the DB too, can be partitioned into independant schemas.

So for example, you need to get a list of users, so you have a service that manages user data. When you need to get a list of organisations too, you add a new service that deals with this and merge the data together in the UI (for example) which means you have not had to alter your user-access code at all. I think this kind of approach is termed microservices, though obviously 'micro' doesn't have to mean tiny, just logically separated.

I would not try to introduce abstractions as all that will happen is these will tie your layers together (eg a DB change will still require the underlying table change, but now the wrapping DAL layer will need change too, and having a DAL will not necessarily let you split the DB into sections, unless you get into a lot more complexity).

8
  • Interesting answer, this sits with my current thinking in that there isn't an easy way to accommodate changes which impact vertically. Annoyingly, although I can divide the changes vertically into stories, the completion of one (or even a collection) of stories, doesn't leave the user in a working state. I'm wondering if the best approach at the moment is to split the changes into 'breaking' and 'non breaking' and incorporate the 'non breaking' into the main development stream, with 'breaking' as it's own arm. Jan 21, 2015 at 14:37
  • 2
    Surely completion of a collection of stories (eg update UI, logic, DB) should leave it in a working state - what else is needed after all! In terms of stories, the story should be "add functionality" and that is them split into 3 tasks - update UI, update Logic, update DB. Never try to use 'breaking' and 'nonbreaking' as you never want a nonbreaking story that turns out to have breaking consequences.
    – gbjbaanb
    Jan 21, 2015 at 14:39
  • 2
    @dougajmcdonald as it is, there is no easy way to accommodate major changes except rolling up your sleeves and getting on with it :-)
    – gbjbaanb
    Jan 21, 2015 at 14:42
  • Yeah I get what you mean, I think this is more than the collection of stories is the entire workpackage, which could take several months. The code, during this phase would be non-deployable but we have other work during this period on the same project which does need to be deployed. As a result, I'm looking for approaches which could allow parallel development and retain the ability to deploy, during a potentially unstable state for 'some' of the code. Jan 21, 2015 at 14:58
  • @dougajmcdonald: In what way would the system be non-deployable? In the sense that it doesn't work in the way the client wants, or in the sense that nothing works at all? Jan 21, 2015 at 15:34
-1

Unit tests and Integration tests can be used to protect your logic from code changes.

  1. If you currently have a good code coverage, and you need to re-work a section, then the approach would be easier, you can change your tests to reflect the result after the re-work and once you have done the change you can re-run the test cases, to make sure it's all working. (In practice it would be a good idea to use a continuous integration server to run tests after each commit, and with this you can do smaller changes at a time and make sure all the logic is intact with the CI server status)

  2. If you do not have tests, then it would be a good idea to create some. If you are under tight deadline, you could start with integration tests where the tests will verify the output of the given input from the presentation level. For each place where change is needed you can start with creating tests for the expected output after the changes, and do the changes so and make sure tests are green.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.