Analyzer

SF0003

Flags ordinary methods whose cyclomatic complexity exceeds 10 so teams do not normalize “hard to reason about” as a routine cost.

Diagnostic contract

Under pressure, complex methods slow diagnosis and increase the chance of editing the wrong branch. That is exactly the problem TwoAmTest is supposed to catch.

SF0003 SimplicityFirst.TwoAmTest Warning Advisory only
When it fires

An ordinary source method calculates above the fixed cyclomatic complexity threshold of 10.

Rule message

Method {0} has cyclomatic complexity {1}, which exceeds the limit of 10

Help link target

/analyzers/sf0003/

When it fires

These are the concrete cases to look for in code review and IDE diagnostics.

  • Only ordinary methods are analyzed; constructors and other method kinds are handled elsewhere or ignored.
  • The analyzer calculates cyclomatic complexity from the method declaration syntax and reports when the score exceeds 10.
  • The diagnostic is advisory today. The right fix depends on extracting intent, not just splitting lines mechanically.

Bad / better example

Use these examples to explain the rule, not just silence it.

Before

public string ResolveStatus(Order order, User user, bool isPriority)
{
    if (order is null) return "missing";
    if (!user.IsActive) return "blocked";

    if (order.IsPaid)
    {
        if (order.IsPacked)
        {
            if (order.IsShipped)
            {
                return order.IsDelayed ? "investigate" : "complete";
            }

            return isPriority ? "expedite" : "dispatch";
        }

        return order.HasBackOrder ? "hold" : "pack";
    }

    return order.RequiresReview ? "review" : "collect-payment";
}

The reader has to simulate several branches before they can answer a routine question.

After

public string ResolveStatus(Order order, User user, bool isPriority)
{
    if (order is null) return "missing";
    if (!user.IsActive) return "blocked";

    return order.IsPaid
        ? ResolvePaidOrderStatus(order, isPriority)
        : ResolveUnpaidOrderStatus(order);
}

private static string ResolvePaidOrderStatus(Order order, bool isPriority)
{
    if (!order.IsPacked) return order.HasBackOrder ? "hold" : "pack";
    if (!order.IsShipped) return isPriority ? "expedite" : "dispatch";
    return order.IsDelayed ? "investigate" : "complete";
}

The branching still exists, but the path names are easier to scan and change under pressure.

Code fix

There is no automatic code fix for this rule today. Treat it as a prompt to simplify deliberately.

  • Start by naming the decision points, not by blindly extracting random blocks.
  • If the method is orchestration, consider moving sub-decisions into narrower collaborators.
  • Use watch or diff to confirm the simplification improves the broader solution signal, not just one method.

Source and follow-up links

Use these links when you need to validate behavior against the source or connect the docs back to project tracking.