this post's header image


Eventually you reach a point where you've thrown every new technology, industry standard best practice, and philosophy at your enterprise scale application, and it is still hard to create new features or manipulate existing ones. At this point you realize that it's not where the code is contained, but the code itself that needs to be improved.

There are lots of things to be done at this point. The biggest impact often comes from the simplest things - naming your variables correctly and outlining major sections of code. Once you start to understand your domain a bit better, sometimes patterns emerge again and again. ExtendedIPO is a pattern I'm finding works over and over again in my application. Whenever I apply this pattern, I find that bugs are easy to diagnose and features are easy to develop. Feel free to use it. Let me know how it goes.


ExtendedIPO has these aims:


I learned about IPO in college. It states that every function has an Input (parameters), Process (the code in the middle) and Output (the value that gets returned). ExtendedIPO redefines these slightly so that the code inside the function follows a specific path.



"Any information that lives outside of the function that the function requires to run." This includes information from databases, caches, etc.


"Any operation which must be completed inside the function before the outside world can be changed."

There are generally two types of processes in CRUD programs, validation and transformation. While not part of Extended IPO, it's recommended that you create one large Process() method for each type of work (validtion, transformation, etc) and then create private methods which the Process() calls for each business rule.


"Any unit of work which is both expected to succeed and changes something in the outside universe."

There is no limit to what could be changed. Perhaps it's a record in a database or a notification sent to a customer. "Expected to work" signifies that you shouldn't return a status code. If something breaks, it's an exceptional case and should be handled by an exception.

Rules gathered from these definitions

Because of how IPO is defined, functions are placed in a specific order. An example of what this looks like in an API lies below.

  1. User sends us data.
  2. We go and get extra data from the database
  3. We make sure that the user submitted data is valid and doesn't conflict with any business rules.
  4. If there are no error messages, transform the data based on any transformational business rules we might have, and submit it to the database.
  5. If there are error messages, return those to the user.

Now that we have an understanding in plain english, let's write it in sudo-C#.

IActionResult PerformUserFlow(userInput) 
    var allData = Query(userInput);
    var errors = Validate(allData);

    if (!errors.Any()) {
        var transformModel = Transform(allData);
        return Ok();
        return BadRequest(errors);

Types of functions

Generalizing the code above, we end up with five different types of functions:

  1. Entry Points: Where the user flow begins (PerformUserFlow(userInput)).
  2. Queries: Where extra information is gathered from the universe (Query(userInput)).
  3. Validators: Where data validation is performed (Validate(allData)).
  4. Transformers: Where information is transformed into something usable (Transform(allData)).
  5. Commands: Where data is saved to the database (Submit(transformModel)).


Our goals as developers should not be to promote any framework or pattern, but to write intention-revealing code. Extended IPO is just one way to write intention revealing code. Also, if you don't prize the "mundane" aspects of programming, such as naming your variables, no amount of new patterns or new language features will help increase your code's readability.

Where to go from here

ExtendedIPO is pretty much an extension of Martin Fowler's CQRS and Jimmy Bogart's Vertical Slice Architecture. These are excellent ideas.

I also have a very small C# library with interfaces representing each of the types of functions... I did this because this is what people seem to do nowadays... But I don't really believe that patterns should use libraries. Anyways, if you want to take a look, it's on GitHub

Respond to this post and join the conversation on DEV