How when AWS was down, we were not

Mike's Notes

I discovered a link to this superb article by Warren Parad in the latest Amazing CTO.

"AWS down, Cloudflare down and companies still advertise 99.99% uptime while using these services. What did the authors do differently? “Simply put — our strategy is to utilize DNS dynamic routing.” Simple. Done that too in the past. BUT that’s not why this article is great. It has a very thorough analysis on SLAs, percentage calculations, failure modes and more. Must read if you want to take SLAs seriously - and not lie to your CEO."

- Amazing CTO

This was a very clever solution. The original article includes many useful links that are missing here.

Resources

References

  • Reference

Repository

  • Home > Ajabbi Research > Library > Subscriptions > Amazing CTO
  • Home > Handbook > 

Last Updated

17/12/2025

How when AWS was down, we were not

By: Warren Parad
Authress: 01/11/2025

Authress CTO • Public Speaker. Adventures in DevOps Host.

AWS us-east-1 is down!

One of the most massive AWS incidents transpired on October 20th. The long story short is that the DNS for DynamoDB was impacted for us-east-1, which created a health event for the entire region. It's the worst incident we've seen in a decade. Disney+, Lyft, McDonald'ss, New York Times, Reddit, and the list goes on were lining up to claim their share too of the spotlight. And we've been watching because our product is part of our customers critical infrastructure. This one graph of the event says it all:

Route 53 Health Check result where us-east-1 is down

The AWS post-incident report indicates that at 7:48 PM UTC DynamoDB had "increased error rates". But this article isn't about AWS, and instead I want to share how exactly we were still up when when AWS was down.

Now you might be thinking: why are you running infra in us-east-1?

And it's true, almost no one should be using us-east-1, unless, well, of course, you are us. And that's because we end up running our infrastructure where our customers are. In theory, practice and theory are the same, but in practice they differ. And if our (or your) customers chose us-east-1 in AWS, then realistically, that means you are also choosing us-east-1 😅.

During this time, us-east-1 was offline, and while we only run a limited amount of infrastructure in the region, we have to run it there because we have customers who want it there. And even without a direct dependency on us-east-1, there are critical services in AWS — CloudFront, Certificate Manager, Lambda@Edge, and IAM — that all have their control planes in that region. Attempting to create distributions or roles at that time were also met with significant issues.

Since there are plenty of articles in the wild talking about what actually happened, why it happened, and why it will continue to happen, I don't need to go into it here. Instead, I'm going to share a dive about exactly what we've built to avoid these exact issues, and what you can do for your applications and platforms as well. In this article, I'll review how we maintain a high SLI to match our SLA reliability commitment even when the infrastructure and services we use don't.

What is reliability?

Before I get to the part where I share how we built one of the most reliable auth solutions available. I want to define reliability. And for us, that's an SLA of five nines. I think that's so extraordinary that the question I want you to keep in mind through this article is: is that actually possible? Is it really achievable to have a service with a five nines SLA? When I say five nines, I mean that 99.999% of the time, our service is up and running as expected by our customers. And to put this into perspective, the red, in the sea of blue, represents just how much time we can be down.

And if you can't see it, it's hiding inside this black dot. It amounts to just five minutes and 15 seconds per year. This pretty much means we have to be up all the time, providing responses and functionality exactly as our customers expect.


But why?

To put it into perspective, it's important to share for a moment, the specific challenges that we face, why we built what we built, and of course why that's relevant. To do that, I need to include some details about what we're building — what Authress actually does. Authress provides login and access control for the software applications that you write — It generates JWTs for your applications. This means:

  • User authentication and authorization
  • User identities
  • Granular role and resource-based authorization (ReBAC, ABAC, TBAC, RBAC, etc...)
  • API keys for your technical customers to interact with your own APIs
  • Machine to machine authentication, or services — if you have a microservice architecture.
  • Audit trails to track the permission changes within your services or expose this to your customers.

And there are of course many more components, that help complete full auth-platform, but they aren't totally relevant to this article, so I'm going to skip over them.

With that, you may already start to be able to see why uptime is so critical for us. We're on the critical path for our customers. It's not inherently true for every single platform, but it is for us. So if our solution is down, then our customer applications are down as well.

If we put the reliability part in the back corner for one second and just think about the features, we can theorize about a potential initial architecture. That is, an architecture that just focuses on the features, how might you build this out as simple as possible? I want to do this, so I can help explain all the issues that we would face with the simple solution.

Maybe you've got a single region, and in that region you have some sort of HTTP router that handles requests and they forward to some compute, serverless, container, or virtual machine, or, and I'm very sorry for the scenario — if you have to use bare metal. Lastly, you're interacting with some database, NoSQL, SQL, or something else, file storage, and maybe there's some async components.

If you take a look at this, it's probably obvious to you (and everyone else) that there is no way it is going to meet our reliability needs. But we have to ask, just exactly how often will there actually be a problem with this architecture? Just building out complexity doesn't directly increase reliability, we need to focus on why this architecture would fail. For us, we use AWS, so I look to the Amazon CTO for guidance, and he's famously quoted as saying, Everything fails all the time.

And AWS's own services are no exception to this. Over the last decade, we've seen numerous incidents:

  • 2014 - Ireland (Partial) - Hardware - Transformer failed - EC2, EBS, and RDS
  • 2016 - Sydney (Partial) - Severe Weather - Power Loss - All Services
  • 2017 - All Regions - Human error - S3 critical servers deleted - S3
  • 2018 - Seoul Region - Human error - DNS resolvers impacted - EC2
  • 2021 - Virginia - Traffic Scaling - Network Control Plane outage - All Services
  • 2021 - California - Traffic Scaling - Network Control Plane outage - All Services
  • 2021 - Frankfurt (Partial) - Fire - Fire Suppression System issues - All Services
  • 2023 - Virginia - Kinesis issues - Scheduling Lambda Invocations impact - Lambda
  • 2023 - Virginia - Networking issues - Operational issue - Lambda, Fargate, API Gateway…
  • 2023 - Oregon (Partial) - Error rates - Dynamodb + 48 services
  • 2024 - Singapore (Partial) - EC2 Autoscaling - EC2
  • 2024 - Virginia (Partial) - Describe API Failures ECS - ECS + 4 services
  • 2024 - Brazil - ISP issues - CloudFront connectivity - CloudFront
  • 2024 - Global - Network connectivity - STS Service
  • 2024 - Virginia - Message size overflow - Kinesis down - Lambda, S3, ECS, CloudWatch, Redshift
  • 2025 - Virginia - Dynamo DB DNS - DynamoDB down - All Services

And any one of these would have caused major problems for us and therefore our customers. And the frequency of incident is actually increasing in time. This shouldn't be a surprise, right? Cloud adoption is increasing over time. The number of services AWS is offering is also increasing. But how impactful are these events? Would single one of them have been a problem for us to actually reach our SLA promise? What would happen if we just trusted AWS and used that to pass through our commitments? Would it be sufficient to achieve 99.999% SLA uptime? Well, let's take a look.

AWS SLA Commitments

The AWS Lambda SLA is below 5 nines


The API Gateway SLA is below 5 nines


The AWS SQS SLA is below 5 nines


Okay, so when it comes to trusting AWS SLAs, it isn't sufficient. At. All.

We can't just use the components that are offered by AWS, and go from there. We fundamentally need to do something more than that. So the question becomes, what exactly must a dependency's reliability be in order for us to utilize it? To answer that question, it's time for a math lesson. Or more specifically, everyone's favorite topic, probabilities.

Let's quickly get through this torture exercise. Fundamentally, you have endpoints in your service, and you get in an HTTP request, and it interacts with some third-party component or API, and then you write the result to a database. For us, this could be an integration such as logging in with Google or with Okta for our customers' enterprise customers.

Calculating the allowed failure rate

So if we want to meet a 5-nines reliability promise, how unreliable could this third-party component actually be? What happens if this component out of the box is only 90% reliable? We'll design a strategy for getting around that.

Uptime is a product of all of the individual probabilities:

P total (Success) P 0 (S)P 1 (S)∗⋯P i (S)⋯∗P C−1 (S)

For the sake of this example, we'll just assume that every other component in our architecture is 100% reliable — That's every line of code, no bugs ever written in our library dependencies, or transitive library dependencies, or the dependencies' dependencies' dependencies, and everything always works exactly as we expect.

P total (Success) 1∗1∗⋯P 3rdParty (Success)∗1∗1

So we can actually rewrite our uptime promise as a result of the failure rate of that third-party component.

P total (Success) 1−P 3rdParty (Failure)

And the only way that we can actually increase the success rate of the uptime based off of failures is to retry. And so we can multiply out the third-party failure rate and retry multiple times.

P total (Success) 1−P 3rdParty (Failure) RetryCount

Logically that makes a lot of sense. When a component fails, if you retry again, and again, the likelihood it will be down every single time approaches zero. And we can generate a really nasty equation from this to actually determine how many exact times do we need to retry.

RetryCount ≥ ln(P 3rdParty (Failure)) / ln(1−P total (Success))

How many exactly can it? Rather than guessing whether or not we should retry four times or five times, or put it in a while(true) loop, we can figure it out exactly. So we take this equation and extend it out a little bit. Plugging in our 90% reliable third-party component:

RetryCount ≥ ln(10%) / ln(1−99.999%)

RetryCount ≥ 5

We find that our retry count actually must be greater than or equal to five. We can see that this adds up to our uptime expectation:

10% (5) = 0.00001⟹99.999%

Is this the end of the story? Just retry a bunch of times and you're good? Well, not exactly. Remember this equation?

P total (Success) = P 0 (S)∗P 1 (S)∗⋯P i (S)⋯∗P C−1 (S)

We do really need to consider every single component that we utilize. And specifically when it comes to the third-party component, we had to execute it by utilizing a retry handler. So we need to consider the addition of the retry handler into our equation. Going back to the initial architecture, instead of what we had before, when there's a failure in that third-party component, now we will automatically execute some sort of asynchronous retries or in-process retries. And every time that third-party component fails, we execute the retry handler and retry again.

This means we need to consider the reliability of that retry handler.


Let's assume we have a really reliable retry handler and that it's even more reliable than our service. I think that's reasonable, and actually required. A retry handler that is less reliable than our stated SLA by default is just as faulty as the third-party component.

Let's consider one with five and a half nines — that's half a nine more reliable than our own SLA.

P retryExecutor (S) = 99.9995%

But how reliable does it really need to be? Well, we can pull in our original equation and realize that our total uptime is the unreliability or the reliability of the third-party component multiplied by the reliability of our retry handler.

P total (Success) = P 3rdParty (S)∗P retryExecutor (S)

From here, we add in the retries to figure out what the result should be:

P totalWithRetries (S) = (P retryExecutor (S)) retryCount

99.999% ≤ (99.9995%) RetryCount

RetryCount ≤ 2

We have a reliable retry handler, but it's not perfect. And with a retry handler that has reliability of five and a half nines, we can retry a maximum two times. Because remember, it has to be reliable every single time we utilize it, as it is a component which can also fail. Which means left with this equation:

5 > 2

I don't think comes as a surprise to anyone that in fact five is greater than two. What is the implication here?

The number of retries required for that unreliable third-party component to be utilized by us exceeds the number of retries actually allowed by our retry handler.

RetriesRequired > RetriesAllowed

That's a failure, the retry handler can only retry twice before itself violates our SLA, but we need to retry five times in order to raise the third-party component reliably up. We can actually figure out what the minimum reliability of a third-party component is allowed to be, when using our retry handler:

Min(P 3rdParty (Success)) = f(RetryHandler)

                                            =1−P 3rdParty  (Failure) RetriesAllowed

                            99.999% ≤ 1−P 3rdParty (Failure) 2

Min(P 3rdParty (Success)) > 99.7%

Which in turn validates that it's actually impossible for us to utilize that component. 99.7%. 99.7% is the minimum allowed reliability for any third-party component in order for us to meet our required 5-nines SLA. This third-party component is so unreliable (~90%), that even using a highly reliable retry handler, we still can't make it reliable enough without the retry handler itself compromising our SLA. We fundamentally need to consider this constraint, when we're building out our architecture.

That means we drop this third-party component. Done.

And then, let's assume we get rid of every flaky component, everything that don't have a high enough reliability for us. At this point, it's good to think, is this sufficient to achieve our 5-nines SLA? Well, it isn't just third-party components we have to be concerned about. We also have to be worried about those AWs infrastructure failures.

Infrastructure Failures

So let's flashback to our initial architecture again:

The simplest auth architecture

We can have issues at the database layer, right? There could be any number of problems here. Maybe it's returning 500s, there are some slow queries, maybe things are timing out. Or there could be a problem with our compute. Maybe it's not scaling up fast enough. We're not getting new infrastructure resources. Sometimes, even AWS is out of bare metal machines when you don't reserve them, request them get them on demand, and the list go on.

Additionally, there could also be some sort of network issue, where requests aren't making it through to us or even throw a DNS resolution error on a request from our users.

AWS Infrastructure Failure locations

In many of these cases, I think the answer is obvious. We just have to declare the whole region as down. And you are probably thinking, well, this is where we failover to somewhere else. No surprise, yeah, this is exactly what we do:

Region failover strategy in AWS

However, this means we have to have all the data and all the infrastructure components duplicated to another region in order to do this. And since Authress has six primary regions around the world, that also means we need multiple backup regions to be able to support the strategy. But this comes with significant wasted resources and wasted compute that we're not even getting to use. Costly! But I'll get to that later.

Knowing a redundant architecture is required is a great first step, but that leaves us having to solve for: how do we actually make the failover happen in practice?

The Failover Routing Strategy

Simply put — our strategy is to utilize DNS dynamic routing. This means requests come into our DNS and it automatically selects between one of two target regions, the primary region that we're utilizing or the failover region in case there's an issue. The critical component of the infrastructure is to switch regions during an incident:

Utilizing Route 53 health checks

In our case, when using AWS, this means using the Route 53 health checks and the Route 53 failover routing policy.

We know how we're gonna do it, but the long pole in the tent is actually knowing that there is even a problem in the first place. A partial answer is to say Have a health check, so of course there is health check here. But the full answer is: have a health check that validates both of the regions, checking if the region is up, or is there an incident? And if it is, reports the results to the DNS router.

We could be utilizing the default provided handler from AWS Route 53 or a third-party component which pings our website, but that's not accurate enough from a standpoint of correctly and knowing for certain that our services are in fact down.

It would be devastating for us to fail over when a secondary region is having worse problems than our primary region. Or what if there's an issue with with network traffic. We wouldn't know if that's an issue of communication between AWS's infrastructure services, or an issue with the default Route 53 health check endpoint, or some entangled problem with how those specifically interact with our code that we're actually utilizing. So it became a requirement to built something ourselves, custom, to actually execute exactly what we need to check.

Here is a representation of what we're doing. It's not exactly what we are doing, but it's close enough to be useful. Health check request come in from the Route 53 Health Check. They call into our APIGW or Load Balancer as a router. The requests are passed to our compute which can interact and validate logic, code, access, and data in the database:

The health check endpoint architecture

The health check executes this code on request that allows us to validate if the region is in fact healthy:

Region HealthCheck validation
import Authorizer from './authorizer.js';
import ModelValidator from './modelValidator.js';
async healthCheck(request) {
    await profiler.start();
    const dynamoDbCheck = accountDatabase.getDefaultAccount();
    const indexerCheck = indexer.authorizationCheck('HealthCheck');
    const sqsValidation = sqsClient.queue('LiveCheck');
    const authorizer = Authorizer.validate();
    const modelValidation = ModelValidator.validate();
    try {
        await Promise.all([
          dynamoDbCheck, indexerCheck, sqsValidation,
          authorizer, modelValidation ]);
    } catch (error) {
        logger.log('HealthCheck Failed', error);
        return { statusCode: 503 };
    }
    await profiler.end();
    return { statusCode: 200 };
}


  1. We start a profiler to know how long our requests are taking.
  2. Then we interact with our databases, as well as validate some secondary components, such as SQS. While issues with secondary components may not always be a reason to failover, they can cause impacts to response time, and those indicators can be used to predict incoming incidents.
  3. From there, we check whether or not the most critical business logic is working correctly. In our case, that's interactions with DynamoDB as well as core authorizer logic. Compared to a simple unit test, this accounts for corruption in a deployment package, as well instances where some subtle differences between regions interact with our code base. We can catch those sorts of problems here, and know that the primary region that we're utilizing, one of the six, is having a problem and automatically update the DNS based on this.
  4. When we're done, we return success or failure so the health check can track changes.

Improving the Failover Strategy

And we don't stop here with our infrastructure failover however. With the current strategy, it's good, in some cases, even sufficient. But it isn't that great. For starters, we have to completely failover. If there's just one component that's problematic, we can't just swap that one out easily, it's all or nothing with the Route 53 health check. So when possible, we push for an edge-optimized architecture. In AWS, this means utilizing AWS CloudFront with AWS Lambda@Edge for compute. This not only helps reduce latency for our customers and their end users, depending on where they are around the world, as a secondary benefit, fundamentally, but it is also an improved failover strategy.

And that looks like this:

CloudFront Edge Failover

Using CloudFront gives us a highly reliable CDN, which routes requests to the locally available compute region. From there, we can interact with the local database. When our database in that region experiences a health incident, we automatically failover, and check the database in a second adjacent region. And when there's a problem there as well, we do it again to a third region. We can do that because when utilizing DynamoDB we have Global Tables configured for authorization configuration. In places where we don't need the data duplicated, we just interact with the table in a different region without replication.

After a third region with an issue, we stop.

And maybe you're asking why three and not four or five or six? Aren't you glad we did the probabilities exercise earlier? Now you can actually figure out why it's three here. But, I'll leave that math as an exercise for you.

As a quick recap, this handles the problems with at the infrastructure level and with third-party components. And if we solve those, is that sufficient for us to achieve our goal the 5-nines SLA?

For us the answer is No, and you might have guessed, if you peaked at the scrollbar or table contents that there are still quite some additional components integrated into our solution. One of them is knowing that at some point, there's going to be a bug in our code, unfortunately.

Application level failures

And that bug will get committed to production, which means we're going to end up with an application failure. It should be obvious that it isn't achievable to write completely bug-free code. Maybe there is someone out there that thinks that, and maybe even that's you, and I believe you that you believe that. However, I know it's not me, and realistically, I don't want to sit around and pray that it's also my fellow team members. The risk is too high, because in the case something does get into production, that means it can impact some of our customers. So instead, let's assume that will happen and design a strategy around it.

So when it does happen, we of course have to trigger our incident response. For us, we send out an email, we post a message on our community and internal communication workspaces, and start an on-call alert. The technology here isn't so relevant, but tools like AWS SES, SQS, SNS, Discord, and emails are involved.

Incidents wake an engineer up, so someone can start to take look at the incident, and most likely the code.

But by the time they even respond to the alert, let alone actually investigate and fix the cause of the incident, we would long violated our SLA. So an alert is not sufficient for us. We need to also implement automation to automatically remediate any of these problems. Now, I'm sure you're thinking, yeah, okay, test automation. You might even be thinking about an LLM agent that can automatically create PRs. (Side note: LLM code generation, doesn't actually work for us, and I'll get to that a little further down) Instead, we have to rely on having sufficient testing in place. And yes, of course we do. We test before deployment. There is no better time to test.

This seems simple and an obvious answer, and I hope that for anyone reading this article it is. Untested code never goes to production. Every line of code is completely tested before it is merged to production, even if it is enabled on some flag. Untested code is never released, it is far too dangerous. Untested code never makes it to production behind some magic flag. Abusing feature flags to make that happen could not be a worse decision for us. And that's because we can need to be as confident as possible before those changes actually get out in front of our customers. The result is — we don't focus on test coverage percentage, but rather test value. That is, which areas provide most value, that are most risky, that we care about being the most reliable for our customers. Those are the ones we focus on testing.

Root Cause Analysis (RCA)

Every incident could have been prevented if we just had one more test. The trick though is actually having that right test, before the incident.

And in reality, that's not actually possible. Having every right test for a service that is constantly changing, while new features are being added, is just unmaintainable. Every additional test we write increases the maintenance burden of our service. Attempting to achieve 100% complete test coverage would require an infinite amount of time. This is known as the Pareto Principle, more commonly the 80-20 rule. If it takes 20% of the time to deliver 80% of the tests, it takes an infinite amount of time to achieve all the tests, and that assumes that the source code isn't changing.

The result is we'll never be able to catch everything. So we can't just optimize for prevention. We also need to optimize for recovery. This conclusion for us means also implementing tests against our deployed production code. One example of this are validation tests.

Validation Tests

A validation test is where you have some data in one format and data in another format and you use those two different formats to ensure referential consistency. (Side note: There are many different kinds of tests, and I do a deep dive in the different types of tests and how they're relevant in building secure and reliable systems). One concrete example could be you have a request that comes in, you end up logging the request data and the response, then you can compare that logged data to what's actually saved in your database.

In our scenario, which focuses on the authorization and permissions enforcement checks, we have multiple databases with similar data. In one case, there's the storage of permissions as well as the storage of the expected checks and the audit trail tracking the creation of those permissions. So we actually have multiple opportunities to compare the data between our databases asynchronously outside of customer critical path usage.

Running the Validation

On a schedule, via an AWS CloudWatch Scheduled Rule, we load the data from our different databases and we compare them against each other to make sure it is consistent. If there is a problem, then if this fires off an incident before any of our customers notice, so that we can actually go in and check what's going on.

The architecture flow to trigger the validation tests

This sounds bad on the surface that it could ever happen. But the reality of the situation is that a discrepancy can show up as a result of any number of mechanisms. For instance, the infrastructure from AWS could have corrupted one of the database shards and what is written to the databases is inconsistent. We know that this can happen as there is no 100% guarantee on database durability, even from AWS. AWS does not guarantee Database Durability, are you assuming they do, because we don't! So actually reading the data back and verifying its internal consistency is something that we must do.

While it might not seem that this could reduce the probability of there being an incident. Consider that a requested user permission check whose result doesn't match our customer's expectation is an incident. It might not always be one that anyone identifies or even becomes aware of, but it nonetheless a problem, just like a publicly exposed S3 is technically an issue, even if no one has exfiltrated the data yet, it doesn't mean the bucket isn'is sufficiently secured.

Incident Impact

There are two parts to the actual risk of an incident. The probability and the impact. Everything in this article I've discuss until now talks about reducing the probability of an incident, that is — the likelihood of it happening. But since we know that we can't avoid ever having an incident, we also have to reduce the impact when it happens.

One way we do that is by utilizing an incremental rollout. Hopefully everyone knows what incremental rollout is, so I'll instead jump straight into how we accomplish it utilizing AWS. And for that we focus again on our solution integrating with CloudFront and our edge architecture.

The solution for us is what I call Customer Deployment Buckets. We bucket individual customers into separate buckets and then deploy to each of the buckets sequentially. If the deployment rolls out without a problem, and it's all green, that is everything works correctly, then we go on to the second bucket and then deploy our code to there, and then the third bucket, and so on and so forth until every single customer has the new version.

Rolling out to customer buckets one at a time

If there is an issue, we stop the rollout and we go and investigate what's actually going on. While we can't prevent the issue from happening to the earlier buckets, we are able to stop that issue from propagating to more customers, having an impact on everyone, and thus reduce the impact of the incident.

As I mentioned before the biggest recurring issue isn't executing an operations process during an incident, it's identifying there is a real incident in the first place. So, How do we actually know that there's an issue?

If it was an easy problem to solve, you would have written a unit task or integration test or service level test and thus already discovered it, right? So adding tests can't, by design, help us. Maybe there's an issue with the deployment itself or during infrastructure creation, but likely that's not what's happening.

Now, I know you're thinking, When is he going to get to AI?

Whether or not we'll ever truly have AI is a separate <rant /> that I won't get into here, so this is the only section on it, I promise. What we actually do is better called anomaly detection. Historically anomaly detection, was what AI always meant, true AI, rather than an LLM or agent in any way.

AI: Anomaly Detection

This is a graph of our detection analysis:

namely detection graph

You might notice that it's not tracking 400s or 500s, which are in reality relatively easy to detect. But in fact don't actually tell us meaningfully what's wrong with our service or whether or not there really is a problem. Impact is measured by business value, not technical protocol level analytics, so we need to have a business-focused metric.

And for us, at Authress, the business-focussed metric we use to identify meaningful incidents we call: The Authorization Ratio. That is the ratio of successful logins and authorizations to ones that are blocked, rejected, timeout or are never completed for some reason.

The above CloudWatch metric display contains this exact ratio, and here in this timeframe represents an instance not too long ago where we got really close to firing off our alert.

Anomaly Detection allowance bands

Here, there was a slight elevation of errors soon after a deployment. The expected ratio was outside of our allowance span for a short period of time. However not long enough to trigger an incident. We still investigated, but it wasn't something that required immediate remediation. And it's a good reminder that identifying problems in any production software isn't so straightforward. To achieve high reliability, we've needed an AI or in this case anomaly detection to actually identify additional problems. And realistically, even with this level of sophistication in place, we still can never know with 100% certainty that there is actually an incident at any moment. And that's because "what is an incident", is actually a philosophical question...

Does it smell like an incident?

Our anomaly detection said – almost an incident, and we determined the result – no incident. But does that mean there wasn't an incident? What makes an incident, how do I define an incident? And is that exact definition ubiquitous, for every system, every engineer, every customer?

Obviously not, and one look at the AWS Health Status Dashboard is all you need to determine that the identification of incidents is based on subjective perspective, rather than objective criteria. What's actually more important is the synthesis of our perspective on the situation and what our customers believe. To see what I mean, let's do a comparison:

incident perspective comparison

I'm going to use Authress as an example. So I've got the product services perspective on one side and our customer's perspective on the other.

Incident Alignment

In the top left corner we have alignment. If we believe that our system is up and working and our customers do, too, then success, all good. Everything's working as expected.

incident perspective comparison alignment

Inversely in the opposite corner, maybe there is a problem. We believe that one of our services is having an issue, and successfully, we're able to identify it. Most importantly, our customers say–yes, there is an issue for us.

It's not great that there's an incident, but as I've identified incidents will absolutely happen, and the fact we've correctly aligned with our customers on the problem's existence independently allows us to deploy automation to automatically remediate the issue. That's a success! If it's a new problem that we haven't seen before, we can even design new automation to fix this. Correctly identifying incidents is challenging, so doing that step correctly, leads itself very well to automation for remediation.

Perspective Mismatch

One interesting corner is when our customers believe that there's nothing wrong, there have been no incidents reported, but all our alerts are saying – RED ALERT — someone has to go look at this!

incident perspective mismatch

In this case, our alerts have identified a problem that no one cares about. This often happens in scenarios where our customers are in one region, Switzerland for example, with local region users, a health care, manufacturing, or e-commerce app, is a good example, rather than global, who are likely asleep at 2:00 AM. And that means an incident at the moment, could be an issue affecting some customers. But if they aren't around to experience it, is it actually happening?

You are probably wincing at that idea. There's a bug, it must be fixed! And sure that's a problem, it's happening and we should take note of what's going on. But we don't need to respond in real time. That's a waste of our resources where we could be investing in other things. Why wake up our engineers based on functionality that no one is using?

I think one of the most interesting categories is in the top right-hand corner where:

  • our customers say, "hey, your service is down"
  • But we say, "Wait, really, is it?"_

This is known as a gray failure.

Gray Failures

Gray failures identified

And it can happen for any number of reasons. Maybe there is something in our knowledge base that tells our customers to do something one way and it's confusing and they've interpreted it in a different way. So there's a different expectation here. That expectation can get codified into customer processes and product services.

Or maybe our customer is running different tests from us, ones that are of course, valuable for their business, but not ones that we consider. Or more likely they are just using a less resilient cloud provider.

Most fundamentally, there could really be an incident, something that we haven't detected yet, but they have. And if we don't respond to that, it could grow, and left unchecked, escalate, and eventually impact all our customers. This means we need to give our customers an easy way to report incidents to us, which we can immediately follow up with.

For us, every single incident, every single customer support ticket that comes into our platform, we immediately and directly send it to our engineering team. Now, I often get pushback on this from other leaders. I'm sure, even you might be thinking something like — I don't want to be on call for customer support incidents. But if you throw additional tiers in your organization between your engineering teams and your customers, that means you're increasing the time to actually start investigating and resolving those problems. If you have two tiers before your engineering team and each tier has its own SLA of 10 minutes to triage the issue, that means you've already gone through 20 minutes before an engineer even knows about it and can go and look at it. That violates our SLA by fourfold before investigation and remediation can even begin.

Instead, in those scenarios, what I actually recommend thinking about is how might you reduce the number of support tickets you receive in aggregate? This is the much more appropriate way to look at the problem. If you are getting support tickets that don't make sense, then you've got to investigate, why did we get this ticket? Do the root cause analysis on the ticket, not just the issue mentioned in it — why the ticket was even created in the first place.

A ticket means: Something is broken. From there, we can figure out, OK, maybe we need to improve our documentation. Or we need to change what we're doing on one of our endpoints. Or we need to change the response error message we're sending. But you can always go deeper.

The customer support advantage

And going deeper, means customer support is critical for us. We consider customer support to be the lifeline of our service level agreement (SLA). If we didn't have that advantage, then we might not have been able to deliver our commitment at all. So much so that we report some of our own CloudWatch custom metrics to our customers so they can have an aggregate view of both what they know internally and what we believe. We do this through our own internal dashboard in our application management UIs.

Authress metric dashboard

Helping our users identify incidents benefits us; because we can't catch everything. It's just not possible.

Negligence and Malice

To this point, we've done the math on reliability of third-party components. We've implemented an automatic region failover and added incremental rollout. And we have a core customer support focus. Is that sufficient to achieve 5-nines of reliability?

If you think yes, then you'd expect the meme pictures now. And, I wish I could say it was enough, but it's not. That's because we also have to deal with negligence and malice.

We're in a privileged position to have numerous security researchers out there on the internet constantly trying to find vulnerabilities within our service. For transparency, I have some of those reports I want to share:

“Real” Vulnerability Reports

fake vulnerability disclosure

I am a web security researcher enthusiast. Do you give a monetary reward?

Okay, this isn't starting out that great. What else have we received?

appeal to ethical hacking rewards

I found some vulnerabilities in your website. Do you offer rewards for ethical hackers?

Well, maybe, but I think you would actually need to answer for us, what the problem actually is. And you also might notice this went to our spam. It didn't even get to our inbox. So a lot of help they might be providing. Actually we ignore any ”security” email sent from a non-custom domain.

Phishing attempt using our own credentials

This one was really interesting. We had someone attempting to phish our engineering team by creating a support ticket and putting in some configuration trying to get us to provide them our own credentials to one of our third-party dependencies. Interestingly enough, our teams don't even have access to those credentials directly.

And, we know this was malicious because the credentials that they are referencing in the support request are from our honey pot, stuck in our UI to explicitly catch these sorts of things. The only way to get these credentials is if they hacked around our UI application and pulled out of the HTML. They aren't readily available any other way. So it was very easy for us to detect that this “report” was actually a social engineering attack.

And this is one of my favorites, and I can't make this up:

Bugbounty vulnerability reporting

I have found many security loophole. How much will you pay if you want to working with me like project?

That's the exact quote, I don't even know what that means. Unfortunately, LLMs will actually start to make all of these future "vulnerability reports" sound more appealing to read in the future, for better or worse. However, at the end of the day, the truth is that these are harmless. And we actually do have a security disclosure program that anyone can go and submit problems for. I hope the message to white-hat hackers is please use that process, and the legitimate reports usually do go through it. Do not send us emails. Those are going to go into the abyss. Alternatively, you can follow our security.txt public page or go to the disclosure form, but with email, the wrong people are going to get that and we can't triage effectively.

Vulnerabilities in our services can result in production incidents for our customers. That means security is part of our SLA. Don't believe me, I'll show you how:

Multitenant considerations

It's relevant for us, that Authress is a multitenant solution. So some of the resources within our service are in fact shared between customers.

Additionally, customers could have multiple services in a microservice architecture or multiple components. And one of these services could theoretically consume all of the resources that we've allocated for that customer. In that scenario, that would cause an incident for that customer. So we need to protect against resource exhaustion Intra-Tenant. Likewise, we have multiple customers. One of those customers could be consuming more resources than we've allocated to the entire tenant. And that could cause an incident across Inter-Tenant and cause an incident across our platform and impact other customers.

Lastly, we have to be worried about our customers, our customers' customers, and our customers' customers' customers, because any one of those could be malicious and consume their resources and so on and so forth, thus causing a cascading failure. A failure due to lack of resources is an incident. The only solution that makes sense for this is, surprise, rate limiting.

Helpful Rate Limiting

So we need to rate-limit these requests at different levels for different kinds of clients, different kinds of users, and we do that within our architecture, at different fundamental levels within our infrastructure.

CloudFront and Region based rate limiting locations

Primarily there are protections at our compute level, as well at the region level, and also place protections at a global level. In AWS, this of course means using a web application firewall or WAF. I think our WAF configuration is interesting and in some ways novel.

Fundamentally, one of the things that we love to use is the AWS managed IP reputation list.

The reputation list is list of IP addresses that have been associated with malicious activity outside of our service throughout other customers at AWS and other providers out there in the world where a problem has been detected. That means before those attacks even get to our service or to our customers' instances of Authress, we can already know to block them, and the WAF does that. This is great, and most importantly, has a very low false positive rate.

However, the false positive rate is an important metric for consideration of counter measures against malicious attacks or negligent accidental abuse of resources, and something that prevents us from using any other managed rules from AWS or external providers. There's two problems with managed rules, fundamentally:

  1. Number one is the false positive rate. If that is even a little bit more than, it couldn't be sustainable, and would result in us blocking legitimate requests coming for a customer. This means it is a problem, and it's an incident for them if some of their users can't utilize their software because of something we did. False positives are customer incidents.
  2. The second one is that managed rules are gratuitously expensive. Lots of companies are building these just to charge you lots of money, and the ROI just doesn't seem to be there. We don't see useful blocks from them.

But the truth is, we need to do something more than just the reputation list rule.

Handling Requests at Scale

And the thing that we've decided to do is — add blocking for sufficiently high requests. By default, any Authress account's service client that goes above 2,000 requests per second (RPS), we just immediately terminate. Now, this isn't every customer, as there are some out there for us that do require such a high load or even higher (as 2k isn't that high). But for the majority of them, if you get to this number and they haven't talked to us about their volume, then it is probably malicious in some way. You don't magically go from zero to 2,000 one day, unless it is an import job.

Likewise, we can actually learn about a problem long before it gets to that scale. We have milestones, and we start reporting loads from clients at 100, 200, 500, 1,000, et cetera. If we see clients hitting these load milestones, we can already start to respond and create an incident for us to investigate before they reach a point where they're consuming all of the resources in our services for that customer. And we do this by adding alerts on the COUNT of requests for WAF metrics.

However, we also get attacks at a smaller scale. Just because we aren't being DDoS-ed doesn't mean there isn't attack. And those requests will still get through because they don't meet our blocking limits. They could be malicious in nature, but only identifiable in aggregate. So while single request might seem fine, if you see the same request 10 times a second, 100 times a second, something is probably wrong. Or if you have request urls that end in .php?admin, when no one has run WordPress in decades, you also know that there's a problem. We catch these by logging all of the blocked requests.

We have automation in place to query those results and update our rules, but a picture is worth a thousand words:

WAF COUNT metrics display

Here you can see a query based off of the IP addresses from the client that are being utilized and sorted by frequency. When we get these requests that look non-malicious individually, we execute a query such as this one and we check to see if the results match a pattern. You can use ip address matching or more intelligently, something called the JA3 or JA4 fingerprints of those requests There are actually lots of options available, I'm not going to get into exactly what they are, there are some great articles on the topic. And there are more mechanisms to actually track these used throughout the security industry, and utilizing them let's you instantly identify: Hey, you know what? This request violates one of our patterns, maybe we should block all the requests from that client.

And so, rather than waiting for them to get to the point where an attacker is consuming 2,000 requests per second worth of resources, you can stop there right away. In the cases where we can't make a conclusive decision, this technology gives us another tool that we can utilize to improve our patterns for the future. Maybe it goes without saying, but of course because we've running our technology to many regions around the world, we have to work on deploying this infrastructure in all these places and push it out to the edge where possible.

Authress AWS Regional and Global locations

The Conclusion

I said a lot of things, so I to quickly want to quickly summarize our architecture that we have in place:

  1. Third-party component reliability reviews. I can't stress this enough. Don't just assume that you can utilize something. And sometimes in order to achieve 5-nines, you actually have to remove components from your infrastructure. Some things are just not able to be utilized no matter what. Now maybe you can put it in some sort of async background, but it can't be on the critical path for your endpoints.
  2. DNS failover and health checks. For places where you have an individual region or availability zone or cluster, having a full backup with a way to conclusively determine what's up and automatically failover is critical.
  3. Edge compute where possible. There's a whole network out there of services that are running on top of the cloud providers, which help guarantee your capability to run as close to as possible to where your users are and reduce latency.
  4. Incremental rollout for when you want to reduce the impact as much as possible.
  5. The Web Application Firewall for handling those malicious requests.
  6. Having a Customer Support Focus to enable escalating issues that outside your area of detection.

And through seven years or so that we've been doing this and building up this architecture, there's a couple of things that we've learned:

Unsolvable Problems at scale

Murphy's Law

Everything fails all the time. There absolutely will be failures everywhere. Every line of code, every component you pull in, every library, there's guaranteed to be a problem in each and everyone of those. And you will for sure have to deal with it, at some point. So being prepared to handle that situation, is something you have to be thinking through in your design.

DNS

DNS, yeah, AWS will say it, everyone out there will say, and now we get to say it. The global DNS architecture is pretty good and reliable for a lot of scenarios, but I worry that it's still a single point of failure in a lot of ways.

Infrastructure as Code (IAC)

The last thing is infrastructure as code challenges. We deploy primary regions, but then there's also the backup regions, which are slightly different from the primary regions, and then there are edge compute, which are, again, even more slightly different. And then sometimes, we do this ridiculous thing, where we deploy infrastructure dedicated to one customers. And in doing so, we're running some sort of IaC to deploy those resources.

It is almost exactly the same architecture. Almost! Because it isn't exactly the same there are quite the opportunities for challenges to sneak it. That's problematic with even Open Tofu or CloudFormation, and often these tools make it more difficult, not less. And good luck to you, if you're still using some else that hasn't been modernized. With those, it's even easier to run into problems and not get it exactly correct.

The last thing I want to leave you with is, well, With all of these, is that actually sufficient to achieve five nines?

No. Our commitment is 5-nines, what we do is in defense of that, just because you do all these things doesn't automatically mean your promise of 5-nines in guaranteed. And you know what, you too can promise a 5-nines SLA without doing anything. You'll likely break your promise, but for us our promise is important, and so this is our defense.

What is Diátaxis and should you be using it with your documentation?

Mike's Notes

I rediscovered this article by Technical Writer Tom Johnson while figuring out how to get the Pipi 9 CMS Engine (cms) to successfully integrate support material and learning documentation into the workspaces.

The actual Pipi 9 code base has no comments. Pipi writes the code and adds a metadata stamp, so why would it need comments? All documentation sits in parallel structures. This closes some doors and opens others.

Pipi 9 uses a structured combination of;

  • Read the Docs
  • Learning Objects
  • Diátaxis
  • The Good Docs Project

Information Pattern

Inspired by Read the Docs.

The whole is composed of Learning Objects in composable hierarchical structures. Each Learning Object

  • Is of one Diátaxis type
  • Uses one Good Docs Template

Learning material is presented in this default order because apparently it works for most people.

Tutorials > How-to guides > Technical reference > Explanation

Users can, however, explore learning material in any way that works for them and set it as a learning preference.

Show, not tell 

In my case, I like to see a diagram, then watch a talk with slides because I'm curious, then read the reference so that I can reverse-engineer to build a working model in my head, then a demo for the ahaha moment to confirm the model. Then an explanation to fill in the gaps, then a how-to guide and finally a tutorial to find the start button.😊 Worked examples are essential, because it gives me a pattern to start with and then change.

We all learn differently.

Ajabbi will need an excellent Technical Writer to improve what I have written, providing top-notch documentation and reducing complexity for readers.

Resources

References

  • Documenting APIs: A guide for technical writers and engineers. By Tom Johnson.

Repository

  • Home > Ajabbi Research > Library > Subscriptions > I'd rather be writing
  • Home > Ajabbi Research > Library > Learning > Learn API Docs
  • Home > Handbook > 

Last Updated

08/12/2025

What is Diátaxis and should you be using it with your documentation?

By: Tom Johnson
I'd rather be writing: 18/10/2023

I'm an API technical writer based in the Seattle area. On this blog, I write about topics related to technical writing and communication — such as software documentation, API documentation, AI, information architecture, content strategy, writing processes, plain language, tech comm careers, and more. Check out my API documentation course if you're looking for more info about documenting APIs. Or see my posts on AI and AI course section for more on the latest in AI and tech comm.

Summary

The Diátaxis approach to documentation organizes technical documentation into four types — tutorials, how-tos, reference, and explanation. In this post, I compare Diátaxis to DITA, Information Mapping, and the Good Docs Project, explaining similarities and differences. I also point out why identifying information patterns can be so worthwhile as a technical writer, and how identifying these patterns not only grounds our practice in the larger practice of rhetoric but also gives us useful patterns to use with AI tools.

Introduction

One topic I keep seeing surface in various places online is Diátaxis. Here are a few places I’ve seen it surface:

  • Upcoming webinars — see Introducing the Diátaxis Approach to Technical Documentation on the Content Wrangler’s BrightTALK
  • PyCon talks — see What nobody tells you about documentation
  • Reddit threads — see Diátaxis, a pragmatic system for technical documentation writing
  • Tutorials that mention it — see API tutorials beyond OpenAPI
  • Discussions among Python doc groups — see Adopting the Diátaxis framework for Python documentation, and more.
  • You can read more about it here: Diátaxis. It seems Diátaxis is gaining some traction in the technical writing community.

I knew Diátaxis involves organizing docs into four specific content types: explanation, reference, tutorials, and tasks. But I was fuzzy on most other specifics. The constant references to Diátaxis made me wonder, what am I missing? Is there something new here? Is this how I should be approaching the structure of my own documentation? And why is Diátaxis growing in popularity? What’s unique about the approach, and how does it differ from DITA?

Diataxis diagram

What is Diátaxis?

The Diátaxis approach divides documentation into four distinct content types:

  • Tutorials - Lessons that provide a learning experience, taking users step-by-step through hands-on exercises to build skills and familiarity.
  • How-To Guides - Practical guides focused on providing the steps to solve real-world problems.
  • Reference - Technical descriptions and factual information about the system, APIs, parameters, etc.
  • Explanation - Background information and conceptual discussions that provide context and illuminate topics more broadly.

The key premise of Diátaxis is that each content type serves a different user need and has a distinct purpose. Keeping them separated allows the content to be tailored and structured appropriately for that specific goal.

What does “Diátaxis” mean?

The name Diátaxis derives from Ancient Greek roots meaning “arrangement” or “layout.” It combines the Greek prefix dia, meaning “across”, with taxis, meaning “arrangement.”

This linguistic root relates to the organizational nature of the Diátaxis documentation approach. At its core, Diátaxis provides guidance for thoughtfully structuring documentation content into different categories based on user needs. The Diátaxis approach takes a jumble of disparate content and systematically arranges it into a meaningful, structured information architecture optimized around user goals.

Why follow the Diátaxis approach?

Here are some key benefits and promises of the Diátaxis approach:

  • Helps users find what they need more easily since content is organized by their goals.
  • Allows writers to focus on the type of content instead of wrestling with how to fit it into a less structured documentation system.
  • Serves both beginners and experts more effectively by separating learning content from reference.
  • Prevents muddling of content types, such as conceptual explanations in a how-to guide or instructions within a reference doc.
  • Provides a consistent, structured model for organizing documentation across products.
  • Promotes better quality content overall by keeping each type focused on its primary user need.

Who is Daniele Procida?

Daniele Procida developed the Diátaxis approach to documentation. Although he has a background in philosophy, Procida has been in the tech industry for a decade now, and has been involved in open-source software for even longer. He is currently an Engineering Director at Canonical.

Comparing Diátaxis with other approaches

As an approach for documentation, Diátaxis shares some similarities with other models.

How does Diátaxis compare with DITA?

DITA users will see quick comparisons with task, concept, and reference, but beyond these overlaps, DITA and Diátaxis actually have a lot of differences:

  • DITA is a formal XML-based standard maintained by the OASIS standards organization. It consists of XML schemas that content must validate against. Diátaxis isn’t an XML schema and doesn’t require validation.
  • DITA has a strong ecosystem of CMS tools and publishing systems built around its XML architecture. Diátaxis is not tied to specific tooling.
  • DITA defines a few more core topic types than Diátaxis, including troubleshooting, glossary, and generic topics. Diátaxis focuses on just four main types: tutorial, how-to, reference, and explanation.
  • DITA allows creating custom topic types through a specialization mechanism. Diátaxis doesn’t have extensibility for new types, as it’s not an XML schema.

Diátaxis has a more prescriptive recommended information architecture based on inherent user needs. DITA is more agnostic about optimal structures. For example, you can assemble the tasks, concepts, and reference types into whatever arrangements you want.

DITA emphasizes content reusability through topic-based authoring. The core idea is that componetizing information into these building blocks allows for efficient reuse across different deliverables (PDF, web, and more). Diátaxis isn’t concerned about content reusability and re-use across multiple outputs.

How does Diátaxis compare with Information Mapping?

DITA also shares some origins with Information Mapping, a technique developed by Robert Horn in the late 1960s. Although many details about Information Mapping are restricted by a paywall, here’s a brief comparison of Diátaxis and information mapping based on the information from Iva Cheung’s Introduction to Information Mapping post.

Cheung says Information Mapping identifies the following information types:

  • procedure—e.g., instructions on how to do something
  • process—e.g., description of how something works
  • principle—e.g., description of a standard or a convention
  • concept—e.g., description of a new idea or object
  • structure—e.g., description of an object’s components
  • fact—e.g., empirical information

You can see similarities here with other approaches. Diátaxis’ explanation is similar to principles and concepts. Reference might related to structure. How-to guides might relate to procedures and processes. I’m not sure what “fact” is, but perhaps it shares intent with tutorials.

Cheung says information management uses these three principles:

  • Chunking: group information into small, manageable chunks.
  • Relevance: limit each group or “unit of information” to a single topic, purpose, or idea.
  • Labelling: give each unit of information a meaningful name.

Here Information Mapping seems more similar to DITA than Diátaxis, in that information is broken down into small chunks with single ideas. The labels, however, seem unique to Information Mapping and interestingly seem to share commonality with embedding techniques used when preparing information for LLMs.

How does Diátaxis compare to The Good Docs project?

Both the Diátaxis approach and The Good Docs Project are focused on identifying best practice patterns and templates for technical documentation, but they approach it differently.

Diátaxis defines 4 core content types — tutorial, how-to, reference, and explanation. In contrast, The Good Docs Project has developed a set of templates mapped to various technical doc types and needs, including:

  • API quickstart
  • API reference
  • Code of conduct
  • Explanation
  • How-to
  • Installation guide
  • Logging
  • Our Team template
  • Overview
  • Quickstart
  • Style guide
  • Release notes
  • Troubleshooting
  • Tutorial

While Diátaxis focuses on the high-level IA, Good Docs provides more tactical templates and writing guides tailored to each type.

Good Docs templates help authors avoid “blank page anxiety” and provide examples with embedded best practices. In contrast, Diátaxis provides more general principles to help guide what should go in each content type.

Why is Diátaxis so popular?

I believe Diataxis is gaining popularity in part because it reveals discernible information patterns within documentation. The approach defines distinct information types, which helps writers recognize structures for organizing content tailored to specific user goals. By defining various information patterns, the Diátaxis helps writers shape and organize content.

The concept of information patterns extends far beyond documentation. For example:

  • Blog posts often follow story patterns.
  • Academic papers have a standard IMRaD pattern (intro, methods, results, discussion).
  • White papers use problem-solution patterns.
  • Knowledge base articles need clear question-focused patterns.
  • Speeches rely on patterns like the three-part list.
  • Legal documents leverage definitions, clauses, sections.
  • Marketing emails use subject lines, preview text, calls-to-action.

Seeing these patterns is like glimpsing the matrix behind communication. Understanding and applying patterns is at the core of rhetoric and document design.

The ability to identify and leverage information patterns in communication is central to the study of rhetoric. Rhetoric is often misunderstood as meaning language intended to manipulate or persuade. But its original definition is using language and information structuring techniques to fit a particular purpose, audience, or situation.

This is why so many technical communication academic programs are housed in rhetoric departments. The rhetorical tradition is fundamentally concerned with patterns of communication — how to shape content to achieve goals like persuading, informing, or motivating an audience.

Technical communicators are modern practitioners of rhetoric. Our job is fitting content to the expected discourse for optimal communication, comprehension, and usability.

Breaking down the Diátaxis information patterns

Let’s briefly look at the information patterns that Procida makes explicit. We’ll look at each information type and describe the salient characteristics.

Diátaxis information patterns

Information patterns in tutorials

In the Diátaxis approach to documentation, a tutorial is “a lesson, safely in the hands of an instructor” that provides a guided, hands-on learning experience.

Whereas DITA focuses on tasks/procedures, tutorials have a different rhetorical shape tailored for education rather than solving problems. Some key elements of a tutorial pattern are as follows:

  • Introduction/Learning goals - States what the user will accomplish in the tutorial.
  • Prerequisites - Lists required knowledge/tools.
  • Step-by-step instructions - Provides hands-on exercises and activities.
  • Recap - Summarizes key lessons and takeaways.
  • Assessment - Questions or exercises to check understanding.
  • Next steps - Points to additional resources for more learning.

This clear rhetorical pattern serves the unique goal of building skills interactively. The lesson shape provides scaffolding and direction ideal for beginners.

(By the way, I’m not sure why “tutorials” isn’t an information type in DITA. The tutorial fills an important niche for learning content in technical communication. Maybe the DITA specification group felt that tasks include tutorials.)

Information patterns in explanation content

In Diátaxis, explanation content provides background and context to illuminate a concept. The goal is deeper understanding rather than problem solving. The following are some common elements of the explanation pattern:

  • Introduction - States the purpose and previews the discussion.
  • Definition - Formal definition of the concept.
  • Background - Provides history and context.
  • Details - Elaborates on various aspects of the concept.
  • Visuals - Diagrams, illustrations, examples that clarify.
  • Relationships - Connects concepts to other ideas and approaches.
  • Implications - Explores effects, outcomes, and significance.
  • Summary - Recaps the key points about the concept.
  • Further Reading - Additional resources to learn more.

This rhetorical structure moves from overview to specifics in order to paint a rich picture of a topic. The goal is synthesizing disparate details into a cohesive narrative about the concept.

Information patterns in reference

In the Diátaxis approach, reference content follows its own distinct pattern serving a dedicated purpose. Procida states, “Reference guides are technical descriptions of the machinery and how to operate it.”

Whereas tutorials focus on learning, reference focuses on factual details about a product or system. Some hallmarks of the reference pattern are:

  • Overviews - High-level summaries of a component’s purpose and capabilities.
  • Specifications - Precise technical details of inputs, outputs, configurations, etc.
  • Options - Charts comparing different options or versions.
  • Parameters - Lists defining all available parameters and their usage.
  • Code samples - Snippets demonstrating implementation and usage.
  • Visuals - Diagrams illustrating technical concepts and relationships.

The reference pattern emphasizes comprehensive, accurate information presentation. The goal is precise and authoritative descriptions users can consult to understand the machinery while working with it.

Information patterns in how-to guides

In Diataxis, how-to guides follow a recipe-like shape focused on solving real-world problems. How-to guides help readers work their way through a problem-field.

Whereas reference provides technical details, how-to guides show practical application. For example, see this topic from the Django documentation: How to configure and use logging.

Some common elements of the how-to pattern include:

  • Prerequisites - Required knowledge or conditions to solve the problem.
  • Intro - If needed, a more detailed description of what the guide will cover.
  • Ordered steps - Sequential instructions to solve the problem, to the extent that’s possible.
  • Visuals - Diagrams and illustrations supporting the steps.
  • Examples - Specific use cases demonstrating the problem and solution.
  • Variations - How the steps may differ under certain conditions.
  • Recap - Summary of what was accomplished.
  • Related links - Pointers to other related procedures.

This structured pattern provides problem-solving assistance distinct from conceptual information. The goal is unambiguous guidance users can follow to solve a particular problem.

Objections about separating content by type

In learning about Diátaxis, I was concerned about the separation of content into distinct groups. Siloing documentation into tutorials, how-tos, reference, and explanation seemed overly opinionated and arbitrary as an information model. What research was this information model based on?

I reached out to Daniele on the #diataxis WTD Slack channel, and he clarified that Diátaxis isn’t meant to impose four rigid buckets that content must squeeze into. Rather, it’s an analytical approach that emerges from identifying four core user needs. In other words, when you identify the user’s information needs, these four fundamental types of documentation arise from those needs: tutorials, explanation, references, and how-to guides.

He acknowledges the critique that people don’t strictly separate these modes, but says that documentation itself should still be clear about its purpose and stabilize around meeting specific user needs.

So the core argument is:

  • Users have different needs from docs.
  • These needs can be mapped to 4 categories.
  • If you write according to those needs, patterns will start to emerge, and …

Thus, 4 types of documentation naturally form to serve those needs.

This model may oversimplify things, but the 4 Diataxis types are still useful as an abstract approach for thinking about docs, even if divisions aren’t absolute in practice.

Experiments at work

In my documentation at work, I recently separated out some concepts by type. For example, when working on reference materials for map data concepts, I initially had conceptual explanations scattered throughout the docs. This made it hard for users to find the conceptual information they needed.

So I decided to group all the conceptual topics into a central “Map Data Concepts” section — like a Wikipedia for map terms and concepts. This created a reliable one-stop shop for reference on key concepts. I now have a space to continue adding more concepts, and I don’t have to worry about reusing similar concepts in different API overviews.

Although I was initially reluctant to separate out concepts from the API overviews, this turned out to be a good move. Both conceptual definitions and overviews probably fit under “Explanation,” but even more granular separation of content by different explanation types seemed helpful.


All-in-one platform to create, share, and manage your documentation

Clear technical documentation, complex translation, and content operations

Will information organization still be necessary when users interface with docs through AI tools?

At this point, note that I’m steering away from explanations of Diátaxis and introducing my own ideas.

With the rise of AI, I think the information architecture of help content may become less critical in the future. Most users will likely interface with documentation primarily through chatbots and other AI tools rather than navigating a complex help system. I wrote about this in AI chat interfaces could become the primary user interface to read documentation

In a way, this reduces the need to obsess over the perfect organizational schema. ChatGPT, Claude, and other AI agents can deliver hyper-personalized help on demand, without requiring the user to know where some piece of information lives in the docs. The AI interface essentially abstracts away the information architecture or pattern and just surfaces the most relevant content dynamically. This makes the documentation’s organization less important from a user perspective.

That said, the actual documentation source still needs to be well-structured for the AI to “learn” from it effectively. But the end user probably won’t care as much, if the AI delivers the right information to them directly.

How can information patterns be used with AI prompting techniques?

In the context of AI, there’s another major benefit to the Diátaxis information model. You can more easily create structured prompts that ask an AI to sort and arrange unstructured information into specific information patterns. I wrote about this in Use cases for AI: Arrange content into information type patterns.

Let’s say that you gather a large body of content about the Widget API from internal documents, code, threads, and more. You can then supply this corpus of unstructured content to an AI and ask it to arrange the relevant information into an information template like this:

{Intro}

{Prerequisites}

{Problem to solve}

{Ordered steps}

{Substeps}

{Examples}

{Expected outcome}

{Related links}

You could even include descriptions of each template section. The AI tool will then pick out the relevant information from the large body of material and arrange it into the pattern you defined. This can significantly speed time to a first draft.

Conclusion

In conclusion, should you be using Diátaxis? Even if you don’t separate content by type, defining and shaping content into these four content types might help improve your documentation. It’s an easy-to-understand approach to documentation that draws power and appeal from this simplicity. You can learn more at https://diataxis.fr.

Related resources

See this BrightTALK video Introducing the Diátaxis Approach to Technical Documentation.

I wrote this post with some AI assistance.

Workspaces for Transport

Mike's Notes

This is where I will keep detailed working notes on creating Workspaces for Transport. Eventually, these will become permanent, better-written documentation stored elsewhere. Hopefully, someone will come up with a better name than this working title.

This replaces the coverage in Industry Workspace written on 13/10/2025.

Testing

The current online mockup is version 2 and will be updated frequently. If you are helping with testing, please remember to delete your browser cache so you see the daily changes. Eventually, a live demo version will be available for field trials.

Learning

(To come)

Why

(To come)

Resources

References

  • Reference

Repository

  • Home > Ajabbi Research > Library >
  • Home > Handbook > 

Last Updated

28/11/2025

Workspaces for Transport

By: Mike Peters
On a Sandy Beach: 28/11/2025

Mike is the inventor and architect of Pipi and the founder of Ajabbi.

Open-source

This open-source SaaS cloud system will be shared on GitHub and GitLab.

Dedication

This workspace is dedicated to the life and work of .


Person

Source: 

"

" - Wikipedia

Change Log

Ver 2 includes aviation, port, road, rail, and shipping.

Existing products

This is a basic comparison of features in transport software.

[TABLE]

Data Model

words

Database Entities

  • Facility
  • Party
  • etc

Shipping Variables

Note that this simulation did not use realistic min, max, mean and std deviation for these calculations. The model itself was the result and will undergo further work and testing using industry data to refine it.

Source: Krombar.ai simulation platform (beta)

Node Name Type Estimates / Formula
Origin inspection pass rate Input Variable beta distribution
Container arrivals Input Variable discrete_normal distribution
40ft container share Input Variable beta distribution
40ft container distance Input Variable Normal(μ=5500.0, σ=2223.903327758403)
Total refrigerated containers scheduled Input Variable discrete_normal distribution
Energy cost per refrigerated container Input Variable Normal(μ=900.0, σ=444.78066555168056)
Fuel cost per 20ft container Input Variable Normal(μ=275.0, σ=185.32527731320025)
Oversized container arrives at origin Calculation Step binomial(round(Total oversized containers scheduled), min(1, max(0, Oversized arrival rate)))
20ft fuel cost Calculation Step 20ft sails to destination * Fuel cost per 20ft container
Ship capacity Input Variable discrete_normal distribution
20ft container share Input Variable beta distribution
Fuel cost per mile per 40ft Input Variable Normal(μ=4.0, σ=1.482602218505602)
Refrigerated arrival rate Input Variable Normal(μ=6.0, σ=5.930408874022408)
Oversized arrival rate Input Variable Normal(μ=2.75, σ=3.3358549916376043)
20ft container arrives at origin Calculation Step binomial(round(Container arrivals), min(1, max(0, 20ft container share)))
Unloading success rate Input Variable beta distribution
Total oversized containers scheduled Input Variable discrete_normal distribution
20ft crew cost Calculation Step 20ft arrival * Crew cost per 20ft container
Refrigerated container arrives at origin Calculation Step binomial(round(Total refrigerated containers scheduled), min(1, max(0, Refrigerated arrival rate)))
40ft fuel cost Calculation Step Refrigerated container arrives at origin*Energy cost per refrigerated container
Destination inspection pass rate Input Variable beta distribution
20ft port fees Calculation Step 20ft arrival * Port fee per 20ft container
40ft sails to destination Calculation Step min(40ft loaded onto ship, round(max(0, Sailing efficiency 40ft * 40ft loaded onto ship)))
40ft crew cost Calculation Step 40ft arrival * Crew cost per 40ft container
20ft checked-in & inspected Calculation Step binomial(20ft container arrives at origin, Origin inspection pass rate)
40ft port fees Calculation Step 40ft arrival * Port fee per 40ft container
20ft loaded onto ship Calculation Step min(20ft checked-in & inspected, Ship capacity)
20ft sails to destination Calculation Step 20ft loaded onto ship
20ft unloaded at destination Calculation Step binomial(20ft sails to destination, Unloading success rate)
20ft checked out & inspected Calculation Step binomial(20ft unloaded at destination, Destination inspection pass rate)
40ft container arrives at origin Calculation Step binomial(round(Container arrivals), min(1, max(0, 40ft container share)))
Energy cost per refrigerated container Input Variable Normal(μ=200.0, σ=91.1854103343465)
Scheduled containers Input Variable discrete_normal distribution
Ship operating cost per container Input Variable Normal(μ=550.0, σ=370.6505546264005)
Port fees Input Variable Normal(μ=150.0, σ=148.26022185056019)
Crew wages Input Variable Normal(μ=125.0, σ=111.19516638792014)
Shipping cost per 20ft container Input Variable Normal(μ=2600.0, σ=2075.6431059078427)
Shipping cost per 40ft container Input Variable Normal(μ=3900.0, σ=3113.4646588617643)
20ft container distance Input Variable Normal(μ=5500.0, σ=2223.903327758403)
Fuel cost per mile per 20ft Input Variable Normal(μ=2.0, σ=0.741301109252801)
20ft container voyage duration Input Variable Normal(μ=25.0, σ=14.82602218505602)
Crew cost per day per 20ft Input Variable Normal(μ=90.0, σ=44.478066555168056)
Number of ports per voyage Input Variable discrete_normal distribution
Port fee per 20ft container Input Variable Normal(μ=275.0, σ=185.32527731320025)
40ft container voyage duration Input Variable Normal(μ=25.0, σ=14.82602218505602)
Crew cost per day per 40ft Input Variable Normal(μ=180.0, σ=88.95613311033611)
Port fee per 40ft container Input Variable Normal(μ=550.0, σ=370.6505546264005)
Fuel cost per refrigerated container Input Variable Normal(μ=650.0, σ=370.6505546264005)
Crew cost per refrigerated container Input Variable Normal(μ=425.0, σ=259.45538823848034)
Port fee per refrigerated container Input Variable Normal(μ=225.0, σ=185.32527731320025)
Fuel cost per oversized container Input Variable Normal(μ=900.0, σ=444.78066555168056)
Crew cost per oversized container Input Variable Normal(μ=650.0, σ=370.6505546264005)
Port fee per oversized container Input Variable Normal(μ=400.0, σ=296.52044370112037)
Special handling rate at origin Input Variable beta distribution
Special handling rate at destination Input Variable beta distribution
Special handling cost per container at origin Input Variable Normal(μ=500.0, σ=444.78066555168056)
Special handling cost per container at destination Input Variable Normal(μ=500.0, σ=444.78066555168056)
refrigerated_arrival Input Variable discrete_normal distribution
oversized_arrival Input Variable discrete_normal distribution
oversized_special_handling_origin Input Variable beta distribution
oversized_special_handling_dest Input Variable beta distribution
refrigerated_fuel_cost Input Variable Normal(μ=550.0, σ=370.6505546264005)
refrigerated_crew_cost Input Variable Normal(μ=275.0, σ=185.32527731320025)
refrigerated_port_fees Input Variable Normal(μ=225.0, σ=185.32527731320025)
refrigerated_energy_cost Input Variable Normal(μ=350.0, σ=222.39033277584028)
oversized_fuel_cost Input Variable Normal(μ=475.0, σ=333.58549916376046)
oversized_crew_cost Input Variable Normal(μ=235.0, σ=170.49925512814423)
oversized_port_fees Input Variable Normal(μ=190.0, σ=163.0862440356162)
oversized_special_handling_cost Input Variable Normal(μ=800.0, σ=593.0408874022407)
20ft_arrival Input Variable discrete_normal distribution
Crew cost per 20ft container Input Variable Normal(μ=140.0, σ=88.95613311033611)
40ft_arrival Input Variable discrete_normal distribution
Fuel cost per 40ft container Input Variable Normal(μ=425.0, σ=259.45538823848034)
Crew cost per 40ft container Input Variable Normal(μ=200.0, σ=148.26022185056019)
20ft_fuel_cost Input Variable Normal(μ=120.0, σ=88.95613311033611)
20ft_crew_cost Input Variable Normal(μ=235.0, σ=170.49925512814423)
20ft_port_fees Input Variable discrete_normal distribution
40ft_fuel_cost Input Variable beta distribution
40ft_crew_cost Input Variable beta distribution
40ft_port_fees Input Variable beta distribution
container_arrival Input Variable beta distribution
Check-in rate at origin Input Variable beta distribution
check_in_inspect_origin Input Variable beta distribution
Loading rate Input Variable beta distribution
load_ship Input Variable beta distribution
Sailing rate Input Variable beta distribution
ship_sails Input Variable Normal(μ=400.0, σ=296.52044370112037)
Unloading rate at destination Input Variable Normal(μ=625.0, σ=407.7156100890405)
unload_dest Input Variable Normal(μ=1250.0, σ=815.431220178081)
Check-out rate at destination Input Variable Normal(μ=1050.0, σ=667.1709983275209)
20ft_ship_cost Input Variable Normal(μ=2500.0, σ=1482.602218505602)
40ft_ship_cost Input Variable Normal(μ=4250.0, σ=2594.5538823848033)
refrigerated_ship_cost Input Variable Normal(μ=6000.0, σ=2965.204437011204)
oversized_ship_cost Input Variable Normal(μ=9000.0, σ=4447.806655516806)
Loading efficiency 40ft Input Variable beta distribution
Sailing efficiency 40ft Input Variable beta distribution
Unloading efficiency 40ft Input Variable beta distribution
Checkout efficiency 40ft Input Variable beta distribution
Loading efficiency refrigerated Input Variable beta distribution
Sailing efficiency refrigerated Input Variable beta distribution
Unloading efficiency refrigerated Input Variable beta distribution
Checkout efficiency refrigerated Input Variable beta distribution
Loading efficiency oversized Input Variable beta distribution
Sailing efficiency oversized Input Variable beta distribution
Unloading efficiency oversized Input Variable beta distribution
Checkout efficiency oversized Input Variable beta distribution
energy_cost_per_refrigerated_container Input Variable Normal(μ=900.0, σ=444.78066555168056)
special_handling_rate_origin Input Variable beta distribution
special_handling_rate_dest Input Variable beta distribution
special_handling_cost_per_container Input Variable Normal(μ=275.0, σ=185.32527731320025)
fuel_cost_per_refrigerated_container Input Variable Normal(μ=1350.0, σ=667.1709983275209)
crew_cost_per_refrigerated_container Input Variable Normal(μ=525.0, σ=259.45538823848034)
port_fee_per_refrigerated_container Input Variable Normal(μ=450.0, σ=222.39033277584028)
fuel_cost_per_oversized_container Input Variable Normal(μ=1600.0, σ=593.0408874022407)
crew_cost_per_oversized_container Input Variable Normal(μ=700.0, σ=296.52044370112037)
port_fee_per_oversized_container Input Variable Normal(μ=600.0, σ=296.52044370112037)
crew_cost_per_20ft_container Input Variable Normal(μ=375.0, σ=185.32527731320025)
port_fee_per_20ft_container Input Variable Normal(μ=250.0, σ=148.26022185056019)
fuel_cost_per_40ft_container Input Variable Normal(μ=1250.0, σ=518.9107764769607)
crew_cost_per_40ft_container Input Variable Normal(μ=475.0, σ=185.32527731320025)
port_fee_per_40ft_container Input Variable Normal(μ=375.0, σ=185.32527731320025)
check_in_rate_origin Input Variable beta distribution
load_rate Input Variable beta distribution
sail_rate Input Variable beta distribution
unload_rate Input Variable beta distribution
check_out_rate_dest Input Variable beta distribution
20ft arrival Input Variable discrete_normal distribution
40ft arrival Input Variable Normal(μ=, σ=)
Oversized checked-in & inspected Calculation Step round(max(0, Oversized container arrives at origin))
Oversized loaded onto ship Calculation Step min(Oversized checked-in & inspected, round(max(0, Loading efficiency oversized * Oversized checked-in & inspected)))
Oversized sails to destination Calculation Step min(Oversized loaded onto ship, round(max(0, Sailing efficiency oversized * Oversized loaded onto ship)))
Oversized unloaded at destination Calculation Step min(Oversized sails to destination, round(max(0, Unloading efficiency oversized * Oversized sails to destination)))
Oversized checked out & inspected Calculation Step min(Oversized unloaded at destination, round(max(0, Checkout efficiency oversized * Oversized unloaded at destination)))
40ft checked-in & inspected Calculation Step round(max(0, 40ft container arrives at origin))
40ft loaded onto ship Calculation Step min(40ft checked-in & inspected, round(max(0, Loading efficiency 40ft * 40ft checked-in & inspected)))
40ft unloaded at destination Calculation Step min(40ft sails to destination, round(max(0, Unloading efficiency 40ft * 40ft sails to destination)))
40ft checked out & inspected Calculation Step min(40ft unloaded at destination, round(max(0, Checkout efficiency 40ft * 40ft unloaded at destination)))
Refrigerated checked-in & inspected Calculation Step round(max(0, Refrigerated container arrives at origin))
Refrigerated loaded onto ship Calculation Step min(Refrigerated checked-in & inspected, round(max(0, Loading efficiency refrigerated * Refrigerated checked-in & inspected)))
Refrigerated sails to destination Calculation Step min(Refrigerated loaded onto ship, round(max(0, Sailing efficiency refrigerated * Refrigerated loaded onto ship)))
Refrigerated unloaded at destination Calculation Step min(Refrigerated sails to destination, round(max(0, Unloading efficiency refrigerated * Refrigerated sails to destination)))
Refrigerated checked out & inspected Calculation Step min(Refrigerated unloaded at destination, round(max(0, Checkout efficiency refrigerated * Refrigerated unloaded at destination)))
Special handling at origin Calculation Step oversized_arrival * special_handling_rate_origin
Special handling at destination Calculation Step oversized_arrival * special_handling_rate_dest
Container arrives at origin port Calculation Step 20ft_arrival + 40ft_arrival + refrigerated_arrival + oversized_arrival
Check-in & Inspect (Origin) Calculation Step container_arrival * check_in_rate_origin
Loaded onto ship Calculation Step check_in_inspect_origin * load_rate
Ship sails to destination Calculation Step load_ship * sail_rate
Unloaded at destination port Calculation Step ship_sails * unload_rate
Check-out & Inspect (Destination) Calculation Step unload_dest * check_out_rate_dest
Refrigerated energy cost Calculation Step refrigerated_arrival * energy_cost_per_refrigerated_container
Oversized special handling cost Calculation Step (oversized_special_handling_origin + oversized_special_handling_dest) * special_handling_cost_per_container
Refrigerated shipping cost Calculation Step refrigerated_fuel_cost + refrigerated_crew_cost + refrigerated_port_fees
Oversized shipping cost Calculation Step oversized_fuel_cost + oversized_crew_cost + oversized_port_fees
Refrigerated fuel cost Calculation Step refrigerated_arrival * fuel_cost_per_refrigerated_container
Refrigerated crew cost Calculation Step refrigerated_arrival * crew_cost_per_refrigerated_container
Refrigerated port fees Calculation Step refrigerated_arrival * port_fee_per_refrigerated_container
Oversized fuel cost Calculation Step oversized_arrival * fuel_cost_per_oversized_container
Oversized crew cost Calculation Step oversized_arrival * crew_cost_per_oversized_container
Oversized port fees Calculation Step oversized_arrival * port_fee_per_oversized_container
20ft shipping cost Calculation Step 20ft_fuel_cost + 20ft_crew_cost + 20ft_port_fees
40ft shipping cost Calculation Step 40ft_fuel_cost + 40ft_crew_cost + 40ft_port_fees
Ship operating costs Calculation Step 20ft_ship_cost + 40ft_ship_cost + refrigerated_ship_cost + oversized_ship_cost + oversized_special_handling_cost + refrigerated_energy_cost

Simulation notes

Formula used

  • Beta Distribution =RAND()*(100-0)+0
  • Log Normal Distribution =RAND()*(100-0)+0
  • Discrete Normal Distribution =RAND()*(100-0)+0

Standards

The workspace needs to comply with all international standards.

  • (To come)

Workspace navigation menu

  • Enterprise Account
    • Applications
      • Aviation v.2)
        • Aircraft
          • Inventory
            • Orders
            • Parts Request
          • Maintenance
            • Fixed Wing
            • Helicopter
          •  MRO (Maintenance, Repair, and Overhaul)
          • Aircraft Records
            • Airframe
            • Engine Cycles
            • Engine Hours
        • Airport
          • Aerodrome
            • Aerodrome Plate
            • AIP
            • Aircraft Stand
            • Apron
            • Helipad
            • Runway
          • Air Traffic Control
            • Frequencies
              • Tower
              • Navigational
            • Slot
          • Contact
          • Emergency
          • Facility 
            • Arrival Departure Board
            • Baggage
            • Boarding
            • Cafe
            • Parking
            • Shop
          • Security
          • Weather
            • ATIS
            • METAR
            • NOTAM
            • TAF
        • Airspace
          • Map
        • Flight
          • Cargo
          • Flight Plan
          • Passenger
      • Rail (v.1)
        • Booking
        • Facility
        • Freight
        • Passenger
        • Rolling Stock
        • Track
        • Road (v.1)
          • Network
          • Shipment
          • Vehicle
        • Shipping (v.2)
          • Port
            • Berth
            • Crane
            • Security
          • Ship
          • TEU
        • Space
          • (To come)
        • Customer (v2)
          • Bookmarks
            • (To come)
          • Support
            • Contact
            • Forum
            • Live Chat
            • Office Hours
            • Requests
            • Tickets
          • (To come)
            • Feature Vote
            • Feedback
            • Surveys
          • Learning
            • Explanation
            • How to Guide
            • Reference
            • Tutorial
          • Settings (v3)
            • Account
            • Billing
            • Deployments
              • Workspaces
                • Modules
                • Plugins
                • Templates
                  • Airport
                  • Freight Depot
                  • Model Railway
                  • Urban Bus
                  • Urban Rail
                • Users

        Workspace trial causes engine modifications

        Mike's Notes

        Changes triggered by the recent successful workspace trial. I slept on this for a few weeks, just to be sure. 😇

        Most of it is done already. A lot better.

        Resources

        References

        • Reference

        Repository

        • Home > Ajabbi Research > Library >
        • Home > Handbook > 

        Last Updated

        7/12/2026

        Workspace trial causes engine modifications

        By: Mike Peters
        On a Sandy Beach: 5/12/2025

        Mike is the inventor and architect of Pipi and the founder of Ajabbi.

        Some Pipi 9 agent engines are getting minor modifications as a result of lessons learned from the recent successful stress-test trial involving 15,000 generated web pages and directories in a workspace website UI. It took creating a massive static mockup for volunteer testers to explore freely and identify gaps.

        After these simple modifications, Pipi 9 will automatically render in about 50 seconds, another static mockup of a huge workspace website for testing. If that stress test is 100% successful, the next stage will be rendering a live version.

        Pipi 9 will then automatically generate much larger custom live workspaces without error. And on demand.

        The necessary minor modifications will change the deployment-workspace hierarchy previously described on this engineering blog.

        The previous workspace settings from 2018, stored as structured training data, also need to be edited. The test results indicate that everything can now be significantly simplified, resulting in a performance boost.

        Deployment Engine (dpl)

        A Deployment Type has been added to create a simple separation of functions and to drive URL pattern creation.

        • Applications a/
        • Customer c/
        • Global g/
        • Settings s/

        Workspace Engine (wsp)

        A new Module Entity has been added to store each workspace module's attributes. They are all inherited from a library of predefined Domain Model Objects.

        The revised hierarchy is

        User Account

        A User Account has three or more Deployments.

        • Applications
        • Customer
        • Settings
        An Enterprise Account can also have Global Deployments to share properties.

        Deployment

        A Deployment is a container for one or more Workspaces. A deployment has these properties;

        • ID
        • Code Name
        • Name
        • Description
        • One language (eg English)
        • One User Account
        • One Deployment Class (type of tenancy)
        • One Deployment Type (type of container)
        • Those properties are inherited by all workspaces.

        Workspace

        A Workspace is a container for one or many Modules. A workspace has these properties;

        • ID
        • Code Name
        • Name
        • Description
        • One inherited language (eg English)
        • One inherited User Account
        • One inherited Deployment
        • One pre-built Domain Model (eg, Screen Production).
        • One Domain Model Template (eg, Feature Film, Documentary, Live Broadcast), These templates can be customised and shared.
        • Main menu (Ribbon, etc)
        Those properties are inherited. This means each Workspace/Domain Model comes with its own set of prebuilt Security Roles and Security Profiles.

        Module

        A Module is a container for nil, one or more modules in a nested tree hierarchy. Modules can be rearranged in the Workspace UI via drag-and-drop. They are all semantically linked. A module has these properties;

        • ID
        • Parent Module ID
        • Code Name
        • Name
        • Description
        • Sort order
        • One inherited language (eg English)
        • One inherited User Account
        • One inherited Deployment
        • One inherited Workspace
        • One pre-built Domain Model Object (eg, Task, Runway, Locomotive, Film Set, Medical Device).
        • Set of prebuilt Security RolesSecurity Profiles.
        • Context menu (Ribbon, etc)
        • Learning objects
        • Contextural help
        • Attached
          • Tools
          • Workflows
          • State
          • Code
          • Data

        Template

        A Template is a prebuilt set of configurations and constraints to customise a workspace. Templates can be saved and shared. A template has these properties;

        • ID
        • One pre-built Domain Model ID
        • Code Name
        • Name
        • Description
        • One language (eg English)
        • Set of prebuilt Security RolesSecurity Profiles.
        • Menu (Ribbon, etc)
        • Set of Modules in a hierarchy
        • Etc