Wednesday, 1 June 2016

Applied Domain-Driven Design (DDD), Part 7 - Read Model

When I first started using DDD I came across a really messy situation. I had my aggregate root and it linked it self to another child aggregate root. Everything worked really well. Shortly after everything was written new requirement came through, I had to expose counts and sums of data based on different filters. This was very painful, I ended up modifying my aggregate roots to try and provide these additional properties. This approach did not perform, for each aggregate root, it was loading another aggregate root with entities and summing them. I've played around with NHibernate mapping files and I've managed to make it performant. By this point I've optimized NHibernate mapping files and my aggregate roots were polluted with query methods. I really didn't like this approach. Shortly after I've came up with another idea, how about we create an immutable model that maps directly to the SQL view and we let the infrastructure handle the mapping? This way our aggregate roots will remain unaffected and we will get much better performance through SQL querying! This is when I have discovered the read model.

In this article we are going to explore how we can end up in this messy situation and why you should use the read model for data mash up and summarisation.

Let's recap how our fictional domain model looks like (omitted to show properties only):
  
    public class Customer : IDomainEntity
    {
        private List<purchase> purchases = new List<purchase>();

        public virtual Guid Id { get; protected set; }
        public virtual string FirstName { get; protected set; }
        public virtual string LastName { get; protected set; }
        public virtual string Email { get; protected set; }

        public virtual ReadOnlyCollection<purchase> Purchases { get { return this.purchases.AsReadOnly(); } }
    }

    public class Purchase
    {
        private List<purchasedproduct> purchasedProducts = new List<purchasedproduct>();

        public Guid Id { get; protected set; }
        public ReadOnlyCollection<purchasedproduct> Products
        {
            get { return purchasedProducts.AsReadOnly(); }
        }
        public DateTime Created { get; protected set; }
        public Customer Customer { get; protected set; }
        public decimal TotalCost { get; protected set; }
    }

    public class PurchasedProduct
    {
        public Purchase Purchase { get; protected set; }
        public Product Product { get; protected set; }
        public int Quantity { get; protected set; }
    }
Please notice the deep relationship between Customer, Purchase and Purchased Product.

New Requirement 
Back office team has just come up with a brand new requirement. They need to get a list of customers that have made purchases, they want to see how much they have spent overall and how many products they have purchased. They are going to contact these customers, thank them for their custom, ask them few questions and give them discount vouchers.

Here is the DTO that we will need to populate and return back via API:
    
    public class CustomerPurchaseHistoryDto
    {
        public Guid CustomerId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public int TotalPurchases { get; set; }
        public int TotalProductsPurchased { get; set; }
        public decimal TotalCost { get; set; }
    }

#Approach 1 - Domain Model DTO Projection
   
        public List<CustomerPurchaseHistoryDto> GetAllCustomerPurchaseHistory()
        {
            IEnumerable<Customer> customers =
                 this.customerRepository.Find(new CustomerPurchasedNProductsSpec(1));

            List<CustomerPurchaseHistoryDto> customersPurchaseHistory =
                new List<CustomerPurchaseHistoryDto>();

            foreach (Customer customer in customers)
            {
                CustomerPurchaseHistoryDto customerPurchaseHistory = new CustomerPurchaseHistoryDto();
                customerPurchaseHistory.CustomerId = customer.Id;
                customerPurchaseHistory.FirstName = customer.FirstName;
                customerPurchaseHistory.LastName = customer.LastName;
                customerPurchaseHistory.Email = customer.Email;
                customerPurchaseHistory.TotalPurchases = customer.Purchases.Count;
                customerPurchaseHistory.TotalProductsPurchased =
                    customer.Purchases.Sum(purchase => purchase.Products.Sum(product => product.Quantity));
                customerPurchaseHistory.TotalCost = customer.Purchases.Sum(purchase => purchase.TotalCost);
                customersPurchaseHistory.Add(customerPurchaseHistory);

            }
            return customersPurchaseHistory;
        } 

With this approach we have to get every customer, for that customer get their purchases, for that purchase get the products that were actually purchased and then sum it all up (lines 16-19). That's a lot of lazy loading. You could fine tune your NHibernate mapping so that it gets all of this data using joins in one go. However that will mean you will be getting unnecessary child data when you are interested only in the parent data (Customer).  Also what if your domain-model is not exposing some of the data that you would like summarise? Now you have to add extra properties to your aggregate roots to make this work. Messy.

#Approach 2 - Infrastructure Read Model Projection 
    
    /*Read only model, I don't think read models should have "readmodel" suffix. 
    We don't suffix Customer, we don't write CustomerDomainModel or CustomerModel we just write Customer. 
    We do this because it's part of the ubiquitous language, same goes for the CustomerPurchaseHistory. 
    I've added this suffix here just to make things more obvious. */
    public class CustomerPurchaseHistoryReadModel
    {
        public Guid CustomerId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int TotalPurchases { get; set; }
        public int TotalProductsPurchased { get; set; }
        public decimal TotalCost { get; set; }
    }

    public List<CustomerPurchaseHistoryDto> GetAllCustomerPurchaseHistory()
    {
        IEnumerable<CustomerPurchaseHistoryReadModel> customersPurchaseHistory =
                this.customerRepository.GetCustomerPurchaseHistory();

        return AutoMapper.Mapper.Map<IEnumerable<CustomerPurchaseHistoryReadModel>, List<CustomerPurchaseHistoryDto>>(customersPurchaseHistory);
    }

    interface ICustomerRepository : IRepository<Customer>
    {
        IEnumerable<CustomerPurchaseHistoryReadModel> GetCustomersPurchaseHistory();
    }

    public class CustomerNHRepository : ICustomerRepository
    {
        public IEnumerable<CustomerPurchaseHistoryReadModel> GetCustomersPurchaseHistory()
        {
            //Here you either call a SQL view, do HQL joins, etc.
            throw new NotImplementedException();
        }
    }

In this example we have created CustomerPurchaseHistoryReadModel which is identical to CustomerPurchaseHistoryDto, which means I can keep things simple and just use AutoMapper to do one to one mapping. I've extended IRepository by creating new interface ICustomerRepository and added custom method GetCustomersPurchaseHistory(). Now I need to fill in CustomerNHRepository.GetCustomersPurchaseHistory() method. As we are now in the infrastructure layer we can just write some custom HQL or query a SQL view.


Summary:
  • Don't use your entities and aggregate roots for properties mush up or summarisation. Create read models where these properties are required. 
  • Infrastructure layer should take care of the mapping. For example, use HQL to project data on to your read model.
  • Reads models are just that, read only models. This is why they are performant and this is why they should have no methods on them and just properties (they are immutable). 

Useful links: 

*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment. 

Saturday, 28 May 2016

JQuery chaining animations with different elements

As I was writing throughput simulator I came across an interesting problem, how do you actually chain JQuery animations with different dynamic elements?



If your code is static, this is simple,  you could just do this:
 
$("#someElement").animate({
    "top": "200px",
    "left": "0px"
}, 2000, function () {
    $("#someElement2").animate({

        "z-index": "-1",
        "top": "100px",
        "left": "0px",
        "width": "220px"

    }, 2000);
});

You are just invoking a function and on complete you are invoking the next function. JQuery has made this very simple for us. Thank you JQuery.

But what if your elements are not static? What if you are adding elements at runtime to HTML and you need to chain these different elements together? How do you achieve same thing in a dynamic way?

Wouldn't it be great if you could do something like this:
    
CompletionChain().Add(function (completed) {
    $("#someElement").animate({
        "top": "200px",
        "left": "0px"
    }, 2000, completed);
})
    .Add(function (completed) {
        $("#someElement2").animate({
            "z-index": "-1",
            "top": "100px",
            "left": "0px",
            "width": "220px"
        }, 2000, completed);
    })
    .Add(function (completed) {
        $("#someElement3").fadeOut(2000).fadeIn(2000, completed);
    })
    .Run(function () {
        //doSomething
    })

This approach allows you to add animations to a queue at runtime and running the entire chain by calling Run(). Unfortunately this is not part of the JQuery API,  however I have written a class to do just that:
    
function CompletionChain() {
    this.chainIndex = 0;
    this.chainList = [];
    this.onComplete = function () { };
                    
    this.Add = function (funcToComplete) {
        var myself = this;
        this.chainList.push(function () {
            funcToComplete(function () {
                myself.chainIndex++;
                myself.execNextFuncInTheChain(myself.chainIndex);
            });

        });
        return myself;
    }

    this.Run = function (onComplete) {
        if (onComplete != null)
            this.onComplete = onComplete;

        this.execNextFuncInTheChain(0);
    }

    this.execNextFuncInTheChain = function (index) {
        if (this.chainList[index] != null) {
            this.chainList[index]();
        } else {
            this.onComplete();
        }
    }

    return this;
} 

The idea is, don't execute the animation straight away, store it in a list. When you are ready invoke Run(), CompletionChain will invoke the first function in the chain and after it completes the first function it will invoke the next function in the chain. It will keep going until it reaches the end and then it will call onComplete. Idea was partially taken from the Linked list algorithm and Command design pattern.

Here it is in action:



*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment. 


Tuesday, 24 May 2016

Agility and lean production throughput simulator with animations (written in JavaScript)

One year ago I have visited one of the most beautiful cities in the world, Florence. While I was there I went inside the Cathedral of Florence. It's amazing, I recommend it.

Cathedral of Florence

Anyway, I am not a travel guide so let's get to the point. This article was inspired by the Cathedral of Florence, why? Queues. I was standing a in a queue for ages and it felt extremely inefficient how they have handled the flow of people. We were standing around for a while, then we would move (it felt random when we moved), some people would stop as they would get out of breath, some people would stop to take pictures, some people would get claustrophobic and start walking back, when you finally get to the top, they use same stairs to go up to the roof and to come down. Roof would also not be jam packed it seemed actually a bit empty. The whole experience just felt extremely inefficient. This is often how software delivery feels like, inefficient and random.

In this article I am going to simulate my experience in the Cathedral of Florence and hopefully convince you that all we need to do is get back to the first principles of good throughput i.e. removing interlocks to zero and reducing wait times.

Simulation Rules:

Simulation was simplified for my and the audience benefit, ball in the queue can either move forward or just stand around. Ball in the queue can't move forward if there is a ball in front of it. If queue is full ball will not be added to the queue. These rules will keep our simulation simple.

Simulation 1: My experience, slow stop start flow

Everyone walking at same speed but stopping randomly.

Average lead time (sec): ...
Running time (sec): ...
Interlocks: ...
Total Arrived: ...
Delivered per second: ...

After 50,000 iterations here are the results 
Average lead time (milliseconds): 6.23
Running time (milliseconds): 930
Interlocks: 604,706
Total arrived: 8,669 (17.3%)
Delivered per millisecond: 9.32

Simulation 2: Probably how it actually was, chaos flow

Everyone walking at random speed and stoping randomly.

Average lead time (sec): ...
Running time (sec): ...
Interlocks: ...
Total Arrived: ...
Delivered per second: ...

After 50,000 iterations here are the results 
Average lead time (milliseconds): 1.93
Running time (milliseconds): 598
Interlocks: 346,437
Total arrived: 9,569 (19.19%)
Delivered per millisecond: 15.994

Simulation 3: How it should have been in the ideal world, single piece flow

Everyone walks at the same speed and doesn't stop as there is no need to stop. 

Average lead time (sec): ...
Running time (sec): ...
Interlocks: ...
Total Arrived: ...
Delivered per second: ...

After 50,000 iterations here are the results 
Average lead time (milliseconds): 3.041
Running time (milliseconds): 1535.21
Interlocks: 0
Total arrived: 49901 (99.8%)
Delivered per millisecond: 32.50

Simulation 4: How it should be in the real world, batch flow

Everyone walks and stops together at the same speed.  

Average lead time (sec): ...
Running time (sec): ...
Interlocks: ...
Total Arrived: ...
Delivered per second: ...

After 50,000 iterations here are the results 
Average lead time (milliseconds): 1.54
Running time (milliseconds): 772.73
Interlocks: 0
Total arrived: 24951 (49.9%)
Delivered per millisecond: 32.28

In this case, simulation 4, batch sequential flow is the most practical flow, and this is exactly how they control Leaning Tower of Pisa queue flow.

Applying this to software delivery

How can we apply this to software engineering? When you look around the office, look at your Kanban board. How does the flow feel like? What do figures say?

In software engineering you can get a lot of unexpected work thrown your way, you might be waiting for another team member to complete something, noise, interruptions, context switching, need to pick up old project to fix some bugs, versions, might be impeded due to build, lack of information, etc. This is all creates wait times and interlocks. Your productivity goes down hill, you are in the office 100% of the time, everyone might be working super hard, but in reality you are being only 17%-19% effective. This is a very scary number. Often managers will not dive deeper and try to create better environment for productivity / effectiveness, instead they just try to hire more people, which just compounds the problem. This is exactly why I believe that small independent teams with just few people can take on big companies, they have less interlocks and wait times. 


Found this useful?
Browse "Throughput Simulator" Repository On Github.


Conclusion

We should be striving towards single piece of flow, it's the most efficient flow. All you need to do is reduce wait times down to zero and remove all interlocks. 

Monday, 8 February 2016

Azure - Web Apps Summary & Cheat Sheet

Over the past month I've been studying towards MCP, Microsoft Azure Developer Specialist certification. I have learned a lot, so I have decided to summarise and visualise the key points.

Features vs Tier


Azure web apps feature vs tier

  • Free - Shared and limited compute, all you get is an Azure URL, some CPU time and access to Kudu to view logs, console, environment information,  processes, web jobs, etc. 
  • Shared  - Shared, limited, but scalable compute, you can even have your own domain name pointing to it.
  • Basic - Dedicated and scalable compute, at this level you are paying per VM and not per web app (more on that later), hence the price jump. This tier is packed with features. 
  • Standard - Dedicated and auto scalable compute with backups and slots for partial traffic routing, quality assurance or seamless upgrades by swapping slots. 
If you are thinking of moving your web apps to the Azure and not sure if it makes sense financially, then you should read Penny Pinching in the Cloud: When do Azure Websites make sense.

What should you know about web apps?


Azure Web Apps Topology


  1. Slots - Standard tier unlocks slot feature. Each web app can have 4 slots and 1 production slot. These non-production slots don’t scale and they tend to reside on the main VM, in our case these slots will remain on WEBVM1. These slots act like production slots and you can have web jobs running inside them. This can be an issue if your application is not designed for multi job processing. To prevent your web job from running just update the config by adding WEBJOBS_STOPPED = 1 to the app settings. Slots are on the same server so don’t do performance testing against them as you will impact performance of the entire VM. If you need to do some performance testing then it would be better to create a new service plan and deploy your web app in to it, this will ensure isolation.
  2. App Service Plan - This is a very powerful feature and it becomes very handy once you go above shared tier. Beyond shared tier you get dedicated compute i.e. your own VM. App service plan = Websites, Features & VM(s) grouping. You pay for a service plan. However when it comes to shared tier you pay per website inside the app service plan. Once you go beyond shared service plan you start paying for VM(s) and not websites. This is important to remember as it can save you a lot of money! So it’s cheaper to pay for 2 websites on a shared service plan (as you not paying for VMs at that point), however it’s much cheaper to pay for 25 websites on a basic service plan as they will be hosted together on a VM.
  3. Dedicated VM - As mentioned above, you don’t get your own VM until you go beyond shared. This means that if you have bunch of websites on a shared tier they could have different IP addresses to each other. Once you upgrade to basic and beyond and if your web apps are inside the same app service plan they will be hosted on a same server and will have the same IP address.
  4. Scalability - When you scale your web app you are scaling the entire app service plan. This means that if you scale mywebsitea.com you are automatically scaling mywebsiteb.com. However you are not scaling any non-production slots. These slots will remain on the main VM, in our case these slots remain active on WEBVM1. To ensure that traffic gets distributed fairly ensure that Application Request Routing is disabled.
  5. Kudu - All web app slots come with Kudu. You can access it via https://{site name}.scm.azurewebsites.net. This tool allows you to execute PowerShell, view real time logs, processes, view environment config, etc.
  6. Web Jobs - Every web app comes with a web job. To see the trace logs just go to https://{site name}.scm.azurewebsites.net/azurejobs/#/jobs. Web jobs can be written in a lot of languages. They can be scheduled, queue triggered or executed on demand. These web jobs are stored inside the site/wwwroot/app_data/jobs and get executed inside the same application pool as the the main site. So if you are hosting mywebsite.com and it's using "application pool 1", well web job for that site will be using exactly the same application pool. Why is this significant? Performance and it seems that something in Azure hits some API that invokes the web job inside the IIS context. Just an interesting observation.


How does slot swap work?

Azure Web App Swap Analysis

"Swap" is not a best name for what actually happens, it should have been called reroute. Why reroute? Because files don't get copied anywhere, config doesn't swap either, all that happens is that host names get changed and specific settings get enforced, that is it. Let's take a closer look:
  1. IIS, App & Diagnostic config - In traditional IIS you could modify virtual directories, error pages, authentication, compression, etc (green box in the picture below) with Azure you can also configure diagnostics. 
  2. Target slot setting override - When you "swap" your config doesn't go anywhere, it stays in the same slot. However there could be a override if you have ticked "Slot setting". Let's say that mywebsite.com has an app setting WEBJOBS_STOPPED = 0 and mywebsite-staging.com app setting is WEBJOBS_STOPPED = 1. I've ticked "Slot setting", this way this setting will always remain the same in that slot. So when I click "swap" it will take mywebsite.com app setting for WEBJOBS_STOPPED and override app setting inside the mywesite-staging.com. Why? Because mywebsite-staging.com will soon become production and in production slot it must always be WEBJOBS_STOPPED = 0.
  3. Warmup - After the config has been overridden with some slot specific config app gets recycled and some external Azure client invokes the app by calling the root directory e.g. mywebsite.com/, this is done to warm up the app.
  4. Reroute - As soon as the website is warmed up IIS host names are changed at the site bindings level. 
  5. Source slot setting override - Now that we have a new production slot up and running the slot setting override is performed against the new staging site. This means that new staging will be down for few seconds while it's gets settings updated, recycled and it starts ups.
By now you should have learnt that app contents and config doesn't get swapped, it's just the host names that get swapped. If you have ticked "slot setting" it also performs specific config override.

For more detailed analysis of slot swap I throughly recommend reading How to warm up Azure Web App during deployment slot swap.

Bringing it all together 


Azure Billing, Resource and Containers Topology


  1. Azure Subscription - Resource and a billing container. You can create resources inside it that span across different regions.
  2. Resource group - Authorisation, billing and a resource container. You can put resources in to a resource group and give users access to just that resource group. 
  3. Sub resource group - Same as a resource group (see #2). 
  4. Service plan - Needs to be with in a subscription and a region. You can't have a service plan that spans across multi regions or subscriptions. 
  5. Region B - Can be used for disaster recovery and performance. To do this you will need to use Traffic Manager. 
  6. Traffic Manager - Used for traffic distribution, failover and routing users to closer region to reduce latency (performance). 
  7. Internal Load Balancer - Routes traffic to all of the available VMs.

Monday, 29 December 2014

Theory Of Constraints And Software Engineering (Improving the throughput)

About one year ago or so I was introduced to the Theory Of Constraints (TOC). It was one of the best things that have ever happened to me as a manager. It changed my view on everything,  It was all thanks to one of my colleagues coming back from a conference and ordering in a book called the "Phoenix Project", the only reason why I was excited about reading it was because of its funky cover (Figure 1).

Figure 1. Phoenix Project Book Cover
As soon as I started to read this book I was instantly hooked, I didn't know at the time that I was going to discover something very important.

In this article I will briefly talk about my understanding of TOC and how I have applied it at work.

Right, let's jump in.

Imagine for a second the following situation. You are an owner of a digital agency, your agency builds web applications only for e-commerce/marketing companies. The following disciplines work for your agency: 10 business analysts, 7 designers,  10 testers, 15 software engineers and 2 web developers. Due to type of work that this agency does (front-end heavy) it should be obvious to us that our constraint in this case will be our web developers. This means no matter how hard other disciplines will work the only work that will be actually shipped to our clients will be what web developers had time to work on. All other work that was done independently by other disciplines will be stored away, waiting for web developers to catch up. This means that your overall throughput is completely constrained by your web developers. 

Let's resolve above situation by using "TOC 5 Focusing Steps" (5FS):
  1. Identify - Find your constraint (you can use Kanban, Utilisation charts, etc), in our case web development is our constraint.
  2. Exploit - Now we need to find out exactly what process this discipline is following, what is making this disciplines life painful, is it a build server? Is IDE not working correctly? Are web developers too involved in the backend development and backend developers should be doing more? In other words offer all the help that you possibly can. Make sure that web developers have always some work to do, after all they are the constraint. 
  3. Subordinate - Make it all about your constraint, only do as much work as a constraint can handle and avoid at all cost the pile up of inventory, this means that disciplines before and after the constraint will be idle. This is not a problem, there will be a much bigger problem if these people are not idle! Now you will need to start to set work in progress limits with buffers, in TOC world it's known as "Drum-Buffer-Rope". 
  4. Elevate - Hire more people in, change the process, transfer some of the existing staff in to the constrained discipline, do what ever you can to break this constraint.
  5. Repeat - By now your constraint should have been broken, so you need to find your next constraint. Don't allow inertia to become a system constraint. 
This video does a great job demonstrating TOC. TOC also focuses on inventory and throughput accounting, I am not going to talk about throughput accounting but I am going to talk about inventory.

Inventory in software engineering is the following:
  • Work In Progress (anything that is being developed in the current iteration)
  • Unassembled or partially assembled work (POCs, Shelved code, Designs, Wireframes, Business requirements, etc)
  • Backlog items / Requirements / Bugs


Figure 2. Lots Of Inventory = Lots Of Waste
This is what happens when you have lots of inventory in the system:
  1. What was produced is no longer needed.
  2. What was produced was wrong.
  3. Need to juggle lots of work at the same  time (context switching / multi-tasking).
  4. What was produced needs to be relearnt again.
  5. It becomes extremely hard to keep track of versions, projects and roadmaps. More processes and bureaucracy will be added to keep everything under "control".
As inventory builds up, waste builds up and it compounds the problem.

One of the ways to remove inventory build up is by cutting out "hand-shake / handover" periods between different disciplines. For this to happen teams need to work together as one unit delivering shippable software every iteration. In the Agile world these teams are also known as feature teams (multidisciplinary teams) they produce inventory just-in-time.

After reading lots about TOC I as a Product Owner have decided to give it a try with one of my feature teams at work.

Here is how we have applied TOC and some other good practices:
  • We have acknowledged testers as our constraint, these guys had to do manual and automation testing (Identify).
  • We have helped them by identifying what problems they were facing most often, in our case it was a build server was taking too long to build and to publish the latest application (Exploit).
  • We have knowingly created a bit of inventory for the next iteration each time, this way testing had something to work on straight away as soon as iteration started, this ensured that they are busy at all times (Exploit).
  • To deliver to testing team faster we have broken our requirements down, this ensured continuous flow (Exploit).
  • Testing was in the loop at all times so there were no surprises with requirements (Communication).
  • We never undertook any work that was vague or unknown (Risk reduction).
  • If we couldn't estimate work or write down a good acceptance criteria for it (because it was unknown) we would create a time boxed investigation task (Risk reduction).
  • If there was a quality issue or something unknown came up during development, team would stop the line and get an answer immediately (Communication).
  • Certain type of work would be done in different parts of iteration, for example at the end of iteration testers would be regression testing, this was an ideal time for other disciplines to do some infrastructure work or training (Subordinate).
  • We did only as much work as testing team could handle (Subordinate).
  • We were looking to hire more testers (Elevate).

Figure 3. Stop The Line.

Above steps have really helped us to improve our throughput, but it also did way more than that. This team was:
  • One of the most efficient teams in the department.
  • Everyone always left on time / no one ever had to do overtime.
  • They have finished ahead of the schedule at worst they have finished on time.
  • As they have finished ahead or on time they had time to groom requirements and get ready for the next iteration (maybe even start work ahead of the schedule).
  • Team has gelled incredibly.
Of course none of this would not have been possible if I didn't have incredible people in the team, their personalities have played the key role.

Summary: 
  • Stop doing work upfront per discipline (creating inventory) and star to work together as one multi-disciplined team (feature team) by using continuous flow / just-in-time delivery.
  • Have your team deliver production ready software every iteration.
  • Break down your requirements in to small deliverables (continuous flow) to achieve full multi-disciplined team utilisation. 
  • Use Kanban board / Utilisation charts to visualise your constraints. 
  • Use TOC 5FS to increase your teams throughput.
  • Ensure that your constraint is never starved of work and is protected from distraction, inefficient processes and bureaucracy.