Back to Blog

Adverse Conditions Testing with Scythe

Sometimes you begin building a tool with a very narrow use case in mind. That was the case when we started designing Scythe. The original intent was straightforward: build a testing framework that allowed us to evaluate our web application against a defined set of TTPs. Over time, however, it became clear that limiting testing to TTPs alone was unnecessarily restrictive.

In reality, anything that could place an application under stress—or cause it to behave unexpectedly—should be testable. TTPs are just one category of adverse conditions. Scalability limits, malformed input, unexpected user behavior, infrastructure constraints, and concurrent failure scenarios are equally important. The real question became: how do we demonstrate, with evidence, that our system will behave correctly under these conditions in production?

This realization fundamentally reshaped Scythe. What began as a TTP-focused testing tool evolved into a general-purpose framework for validating application behavior under adverse conditions. Can the infrastructure scale with demand? Can the system tolerate malformed or malicious input? Can it handle unexpected user volume or multiple failure modes simultaneously? And critically—what proof do we have?

The answer, of course, is testing—but not just one kind. Modern software teams already rely on multiple layers of testing:

  • Unit tests, which validate that individual functions behave as expected
  • Integration tests, which verify interoperability with external systems such as Postgres, Redis, or S3
  • Penetration testing, where human testers actively attempt to break the system

Each of these plays an important role in the software development lifecycle. What's often missing, however, is a systematic way to test resilience and adverse behavior as part of CI. We have static code analysis—so why not resiliency analysis?

This is the gap Scythe is designed to fill: an extensible framework that allows teams to define and test not only threat scenarios, but a wide range of adverse conditions as code.


Journeys as a Core Primitive

One of the primary abstractions in Scythe is the Journey. A Journey represents a user workflow and is composed of steps, which in turn consist of actions. This structure allows us to model real user behavior with precision.

For example, a user journey might include:

  • Logging into the application
  • Filling out a form
  • Uploading a file
  • Changing a password
  • Updating contact information
  • Modifying a StellarBridge subscription

Any meaningful interaction with the application can be represented as a Journey.

Journeys can be executed independently or chained together to simulate complex, end-to-end workflows. This aligns with our broader philosophy of everything as code: configuration as code, infrastructure as code, and now platform testing as code.

To illustrate, consider a "login" step within a Journey. While it appears simple, it may involve multiple actions, such as:

  • Moving the mouse and clicking the dashboard button
  • Selecting the username field and entering a username
  • Selecting the password field, entering a password, and submitting the form

By breaking behavior down this way, we gain fine-grained control over how tests are defined and executed.


Pass/Fail Semantics for Adversarial Testing

We also rethought how test outcomes are evaluated within Scythe. Instead of treating failure as inherently negative, Scythe adopts the pass/fail semantics developers are already familiar with from unit testing—but applied to expected behavior.

When defining a Scythe test, engineers specify whether a given action is expected to succeed or fail. For example:

  • If you attempt to log in with invalid credentials, the expected result is failure
  • If SQL injection is attempted via a form, the expected result is rejection

If the application behaves as expected—by denying access or rejecting malicious input—the test is considered a pass, even though the action itself failed. This allows security and resilience scenarios to be treated as first-class test cases rather than special exceptions.


We'll be publishing a follow-up article soon that dives into the new Scythe Orchestrator feature.

Interested in contributing? Check out the repository: https://github.com/EpykLab/scythe