3

I ask this question based on the fact that currently my environment is under constant change due to the type of work we do. We do not always work on a project bases we often have smaller changes that just go through a quick change request process to trigger a developer to make the necessary changes. But my question relates more to the actual programming and anticipating that this program will be enhanced and modified in the future and often not by the same developer.

Is there a strategy or a way programmers themselves can ensure there program can stand up to quick effortless modifications? Right now I am trying to ensure any developments are broken down into core function processes and function modules or objects are created to perform each process. This means that the final program is actually more a list of function calls for the various process steps. This ensures you can easily change processes called or function calls to enhance or change the code easily. This helps especially when additional validations and checks need to be built in in-between process steps .

This has its downfalls though, so this leads me to the question "How does a developer code in anticipation of change?", how would you code ensuring the next person can easily and quickly change or enhance your code?

W.Walford
  • 87
  • 1
  • 5
  • Well, being able to evolve the system is pretty much just "good design", isn't it? – Jörg W Mittag Jan 08 '15 at 06:21
  • 1
    http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29 is a good place to start. But honestly, there are books filled with that topic, and such questions don't fit well to the Q&A format of this site, there are too broad. – Doc Brown Jan 08 '15 at 07:56
  • possible duplicate of [How would you know if you've written readable and easily maintainable code?](http://programmers.stackexchange.com/questions/141005/how-would-you-know-if-youve-written-readable-and-easily-maintainable-code) – gnat Jan 08 '15 at 08:08
  • Thanks all for the comments, @jörg-w-mittag yes it is good design but the programmer themselves don't typically do the design. So I am more looking for code specific. ie: using more includes, have a constant way of commenting on the include and the referencing file, restricting amount of code ect. I think I have the answer or a good place to start when looking at the link from gnat and the answer from MainMa. – W.Walford Jan 08 '15 at 08:41
  • 1
    @gnat for most people this seems not to be a duplicate, but experienced programmers will agree. – Wolf Jan 08 '15 at 10:36
  • 1
    "but the programmer themselves don't typically do the design" - sounds to me like you have a pretty big misconception of what "design" actually means in programming. Especially the part of design which makes the difference between evolvable code and non-evolvable code. Maybe http://www.developerdotstar.com/mag/articles/reeves_design_main.html helps you to give you a better undestanding. – Doc Brown Jan 08 '15 at 14:26

4 Answers4

9

Two very essential things to understand are that:

  • You can never anticipate every change a customer may ask. I had a customer who decided to switch a two months project from PHP to ASP.NET one week before release and was convinced that this would be an easy change.

  • Any change will have a cost. It doesn't matter if you are using Agile or if you have clean and extensible design, the cost will still be there.

Having said that, there are multiple techniques which lead to less expensive changes. As already noted by Jörg W Mittag, this is pretty much just "good design", but if you want something more specific, here are some hints:

  1. Avoid code duplication at all costs. Having to make a change in this class, then in this one, and finally there—be careful, the code there is not exactly the same as in the first two locations—would increase the cost.

    The major problem here is that on large projects, developers cannot possibly know which pieces were already written and may develop their own in a different location in the code base. Clean architecture and proper documentation helps, but doesn't make the problem disappear.

  2. Your system should be decoupled as much as possible. If a small change in the module which displays generated invoices on the screen requires to rewrite a few classes in the module which handles registration of new users, there is something wrong with decoupling.

    • Decoupling may be done through interfaces. This means that you can work on the underlying logic while the interface remains the same and other parts of the system are unaffected. For example, you may have an interface for a logger component; when your customer asks to switch from syslog to a RabbitMQ-based message queue, your changes are constrained to the class which implements the given interface, and the users of this interface don't really care where the logs go.

      The major problem here is that interfaces may be leaky. For example, you move from syslog to RabbitMQ, and then notice a new type of exceptions when message queue service is unreachable. Classes using the logging interface should now handle this additional exception.

    • Additional decoupling may be achieved with Dependency injection. The benefit is that instead of working on the class itself, you create a new one, test it separately, and when ready, swap the old class with the new one, while keeping the possibility to go back to the old class seamlessly (either by modifying a single line of code, or through configuration).

    The cons is that this requires more work. If used too much (every part of the application is injected), the design may become too complicated.

  3. Environment matters as much as design. Some hints:

    • Your code should be maintainable. Spaghetti code is problematic not by itself, but specifically because it makes it difficult to modify the code base.

    • Uniform style matters, because it makes the code easier to read; difficult to read code is difficult to change as well.

    • Automated regression testing makes maintenance less stressful. A year ago I worked in a company where the key product had 0% code coverage. When somebody had to make a change, he made it, and then if something bad happened in production, the programmer was the culprit. This is an excellent example of how not to anticipate changes.

      The problem is that tests should be maintained as well, so instead of simply changing code, one should change tests and code. The time spent changing tests is nothing compared to the time wasted because of the lack of proper testing, but inexperienced project managers may not understand that.

    • All members of the team should be aware of all parts of the code base (of the product, not the whole company).

Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
  • I long and well-reasoned answer, but it seems hard not getting lost in these *multiple techniques*, so I tried [a shorter answer](http://programmers.stackexchange.com/a/269409/106929). – Wolf Jan 08 '15 at 10:41
  • 1
    You forgot: Write tests so you can quickly verify that your change didn't break stuff. – Wilbert Jan 08 '15 at 10:49
2

I'm not advocating this as the answer, but this is a list of techniques that has helped me greatly over the past 20 or so years.

Self documenting code

You might be tempted to add comments everywhere to make the code easier to understand. But the simple fact is that a lot of the time, if extensive comments are required within a method, the code isn't simple enough and should be refactored.

KISS

Modules or methods that do a lot are a code smell and should be refactored as far as possible to make them easier to read, understand and modify.

YAGNI

With change in mind it is tempting to add a whole raft of bells and whistles which might save time later. Resist the urge to do this. Extra code requires testing, can contain bugs and makes your code base larger.

Path of least resistance

If you are faced with a change and the customer doesn't know exactly what they want, code the knowns and either wireframe or mock the rest. The last thing you want to be doing is say, coding 4 options when only 1 is ever going to be kept.

Mock

As mentioned above, if you can mock data or screens rather than code them, this will save time.

Slay sacred cows

Any developer who has been round a while has worked on systems where there is a business critical complex piece of code that nobody wants to touch under pain of death! If the code is changed, it often breaks, behaves in unanticipated ways or technical debt is added to code round it.

Firstly, make sure you aren't creating any sacred cows. Secondly, if any exist, break them down so they're more easily digestible. Automated unit tests can assist greatly here.

Robbie Dee
  • 9,717
  • 2
  • 23
  • 53
  • **YAGNI** definitely. I read *Extra code requires testing, can contain bugs and makes your code base larger* as *Extra code makes your code base larger, and most probably contains bugs, because no one takes the burden to justify extra testing.* – Wolf Jan 08 '15 at 14:55
  • Interestingly, I've just been reading about [Lean Software Development](http://en.wikipedia.org/wiki/Lean_software_development) which seems to cover much of what I myself have gleaned. – Robbie Dee Jan 09 '15 at 11:04
2

There are plenty of methodologies given for making your code more adaptable and maintainable, but most of them have the following points at their core:

  • Keep methods small: Your methods should only do one thing well. If they try to do more than one thing they will almost certainly fail at doing at least one of those things under certain circumstances, and they definitely will grow very complex warty internal logic. For this reason you should also keep the number of arguments passed in to a minimum. As well as making the code simpler to comprehend (and therefore adapt), it also makes reuse much easier. If you've got a 500 line method with 10 lines of logic somewhere inside that you need to reuse in a different circumstance, then you have to either add a flag argument and wrap that logic (or the surrounding logic) in if statements, or copy and paste it somewhere else. If that logic was all in its own 10 line method, you can just call it.
  • Keep your classes small and focused. Just like with methods, classes should only do one thing well. For example on our e-commerce platform the order class contains a lot of logic for order delivery. We've recently needed to add functionality to allow a single order's components to ship from multiple locations (should some of the stock be at one location and the rest at a different one). Because delivery was so tightly coupled with the concept of an order, eventually the only viable solution was to build convoluted logic to analyse the order and create multiple orders based on the original, each mapped to ship from the location with the stock, and kludge and work around the issues related to payment for that order, which are also embedded in the order class. In short our order class tries to do too much and its responsibilities aren't clear. If we had separate order payment classes and order delivery classes that payment and delivery were delegated to, the work would have been far simpler and would most likely simply been a matter of adapting the order class to support multiple deliveries per order.

In software, small is always beautiful, and if your code is too big to fit on your screen without scrolling then it's probably also too big to fit in your comprehension.

GordonM
  • 6,295
  • 1
  • 21
  • 30
-1

As MainMa already said: The changes that actual happen are unpredictable. (in almost all cases)

Keep the implementation as simple as possible from the technically POV and as close as possible to the actual requirements (i.e. use speaking identifiers), or, in other words

The best you can do for future changes is help understanding how your solution works.

Wolf
  • 630
  • 1
  • 6
  • 24