Question

When writing a long process, i.e. one filled with many steps of business logic, what are the best practices for organising it? There are a few different options here that I can see:

  1. Just write a long script - however steps aren't modular or reusable
  2. Write each step as a function and call each in order in the main method - good for visualising the whole script logic, better for unit testing. You might end up with many singleton functions though, and the whole environment needs to be passed to each function every time.
  3. Write a long script, and turn parts of code into functions if another process uses them - difficult to visualise and remember what functions exist when writing other processes
  4. Create a singleton class that has methods to be called in order by the class init process - reuses class scope variables but impossible to unit test
  5. Create a singleton class that has methods to be called in order externally - reuses class scope variables and easier to unit test, but then each method relies on it being called in a certain order which seems to disobey the best practice of having methods uncoupled.
  6. Create a singleton class that has methods to be called in order by a "main process" method - reuses class scope variables but same issues as 5)
  7. Review the whole process, as this very question screams that it is being come at the wrong way in the first place
Was it helpful?

Solution

Donkey, Cart... Cart, Donkey

Context matters here. There is rarely a best way to organise anything, let alone code.

Organisation isn't a standalone property. You organise something to achieve an end. So what are those ends?

Generally there a 3 kinds of End:

  1. Functional
  2. Operational
  3. Developmental

Functional just means that it achieves the business goal. Anyone of your suggested patterns of organisation are equally capable of providing that functionality until we get into the performance edge cases, where some of them will will run into platform limitations such as: stack height, execution overhead, core utilisation, etc...

Operational just means how many times it has to be prodded by a human in production to get it back to working. Again none of these suggested patterns make it easier or harder from an operational perspective. Using service(micro or otherwise) architecture vs monolithic does have an impact operationally. How buggy the code is, or even how many edge cases are covered in code have an effect on operations.

Developmental is about how easy it is to make changes, both from a rate of change, and from a reliability of change perspective. Here is where your suggested organisations have an effect. But what exact end are you optimising for?

  • Code isolation (reduce the chance the code change cascades)
  • Code sharing (reduce the amount of distributed knowledge in the system)
  • Code verifiability (reduce the complexity of each piece of code to allow easy testing)
  • Some other developmental concern? Readability, Ramp up time, modularity, etc...

Or are you wanting some balance?


Missing the Point

Personally if you do not know, start with a high level sketch and see if there are any obvious Library functions you will need. eg: you may need to access data from a file-share although the file name and credentials vary.

Next look for any sub-business behaviours that are often performed in the process. Things like getting boss sign-off are often shareable pieces of business logic.

Finally don't push it. If nothing leaps out as being reusable don't force it. Use your first suggestion. Dump it in a single function. Just don't leave it that way.

Start re-factoring by pulling out concerns, or pushing down details. At the end of the day the business function should read like a page from a manual that any other employee could follow. The details pushed down, or pulled out should be independently testable.

When you go to write your next piece of business logic, you will need to read your code base to find out what has already been done in that area. You will then need to determine if the code should be shared, generalised, duplicated, or written differently.

What you'll hopefully end up with is a set of library functions for performing standard low level operations such as authenticating, load files using credentials, etc...

On top of this you'll have a set of common business tasks like getting manager approval, generating an advice, queuing work for manual review. These will be non-trivial pieces of business logic in their own right, but are not a complete business workflow. Internally they will have numerous one off support functions. And make use of the underlying library functions to perform actions.

And on top of this you will have high level business workflows such as terminating a client application, amending a contract, or charging monthly fees. These will be built by using the stock standard business task, but also with one-off support functions that use the library functions to implement workflow specific behaviours.

You will generate mess, its unavoidable as you probably don't yet know what you are optimising the business logic for. And there is no-architecture that will save you from this. The important part is that your writing process is as much a rewriting process, tidying up, cordoning off, and fixing akward pieces of code overtime. Therefore the optimal way to write, is to write to rewrite.

Licensed under: CC-BY-SA with attribution
scroll top