autohand "Set up an Evolve pipeline that runs nightly to identify code smells, suggest refactoring opportunities, and create PRs for approved improvements."

What you'll learn

  • How to write evolution rules that target specific code improvement patterns
  • How to configure a scheduled Evolve pipeline with cron syntax
  • How to review, approve, and auto-merge Evolve-generated pull requests
  • How to create custom evolution targets for project-specific patterns

Before you start

  • Autohand Code installed
  • An Autohand Team or Enterprise account (Evolve uses scheduled background agents; contact sales)
  • A GitHub or GitLab repository with a token that can create branches and open pull requests
  • A passing test suite (Evolve only creates PRs when tests pass)
  • An AGENTS.md with project context so Evolve follows your conventions

Tip: Run Evolve manually a few times before scheduling it. The first few runs let you calibrate the evolution rules and review quality before you start waking up to automated PR notifications every morning.

What is Autohand Evolve

Evolve is a scheduled improvement loop. Each run follows the same cycle: analyze your codebase against your evolution rules, identify areas that match an improvement target, implement changes in an isolated branch, run your tests, and open a pull request if tests pass.

What makes Evolve different from a one-shot refactoring session is that it accumulates improvements incrementally over weeks and months. Each PR is small and focused on one type of improvement. Your team reviews and merges them at their own pace. Over time the codebase gradually trends toward the quality targets you defined.

Typical evolution targets include:

  • Functions longer than 50 lines that can be extracted into smaller functions
  • Duplicated logic across modules that belongs in a shared utility
  • Missing error handling in async functions that currently let rejections go unhandled
  • Inconsistent naming conventions across files written at different times
  • Outdated API usage where a newer, simpler approach is available

Configure the evolution rules

Evolution rules live in a file at the root of your project called .autohand/evolve.json. Create it with Autohand.

bash

autohand "Create an .autohand/evolve.json configuration file for this project.
Read the codebase to identify the three most impactful improvement targets.
For each target, write an evolution rule with a name, description, match criteria,
and the transformation to apply. Prioritize changes that are low-risk and testable."

Here is a representative evolve.json for a Node.js API project.

json

{
  "version": "1",
  "schedule": "0 2 * * *",
  "max_prs_per_run": 2,
  "base_branch": "main",
  "pr_labels": ["autohand-evolve", "refactor"],
  "rules": [
    {
      "name": "extract-long-functions",
      "description": "Extract functions longer than 50 lines into smaller, named functions",
      "enabled": true,
      "target": {
        "type": "code-smell",
        "pattern": "functions with more than 50 lines",
        "paths": ["src/"],
        "exclude_paths": ["src/generated/", "src/__tests__/"]
      },
      "transformation": {
        "instruction": "Extract this function into smaller, well-named helper functions. Each helper should do one thing. Keep the original function as the public entry point but delegate to the helpers.",
        "max_files_per_pr": 3,
        "require_tests_pass": true
      }
    },
    {
      "name": "add-missing-error-handling",
      "description": "Add try/catch blocks to async functions that currently have none",
      "enabled": true,
      "target": {
        "type": "missing-pattern",
        "pattern": "async functions without error handling",
        "paths": ["src/routes/", "src/services/"],
        "exclude_paths": []
      },
      "transformation": {
        "instruction": "Add appropriate error handling. Catch errors, log them with the existing logger, and return a structured error response. Match the error response format used in other routes in this project.",
        "max_files_per_pr": 5,
        "require_tests_pass": true
      }
    },
    {
      "name": "consolidate-duplicate-utilities",
      "description": "Identify duplicated utility functions and consolidate them into shared modules",
      "enabled": true,
      "target": {
        "type": "duplication",
        "similarity_threshold": 0.8,
        "paths": ["src/"],
        "exclude_paths": ["src/generated/"]
      },
      "transformation": {
        "instruction": "Move the duplicated logic to the appropriate shared module in src/utils/. Update all callers to import from the shared location. Do not change behavior.",
        "max_files_per_pr": 8,
        "require_tests_pass": true
      }
    }
  ]
}

Set up the schedule

The schedule field in evolve.json uses standard cron syntax. "0 2 * * *" means 2:00 AM UTC every day.

For most teams, a nightly run is the right cadence. It gives Evolve time to find improvements without flooding your PR queue. Some teams prefer weekly runs on Sunday night so the queue is fresh at the start of the work week.

text

Nightly at 2 AM UTC:   "0 2 * * *"
Weekly Sunday night:   "0 2 * * 0"
Twice weekly:          "0 2 * * 1,4"
Weekdays only:         "0 2 * * 1-5"

Register the pipeline with the Autohand platform using the CLI.

bash

# Authenticate if you haven't already
autohand login

# Register the Evolve pipeline for this repository
autohand evolve init --repo github.com/your-org/your-repo

# Verify the pipeline is registered
autohand evolve status

After evolve init, Autohand adds a webhook to your repository. This allows it to trigger runs on schedule and post status updates when a PR is created.

Review generated proposals

After the first run, Evolve opens pull requests against your base branch. Each PR targets one rule and includes a description of what was changed and why.

A typical Evolve PR description looks like this.

markdown

## Autohand Evolve: extract-long-functions

Extracted `processPaymentBatch` (87 lines) into four focused helpers.

### Changes
- `src/services/payments.js`: extracted `validateBatchItems`, `chargeSingleItem`,
  `handleChargeError`, and `buildBatchResult` from `processPaymentBatch`
- All existing tests pass
- No behavior changes

### Why
`processPaymentBatch` was 87 lines and mixed validation, charging, error handling,
and result assembly. Each extracted function now has a single responsibility and
is independently testable.

---
Generated by Autohand Evolve | Rule: extract-long-functions | Run: 2026-03-12T02:00:00Z

Review these PRs the same way you review human-authored refactoring PRs. Check that the behavior is unchanged, that the new function names are clear, and that the split makes logical sense. Approve and merge the ones you agree with. Close the ones you do not.

Tip: If you consistently reject PRs from a specific rule, either tighten the rule's match criteria or disable it. Evolve learns from your merge patterns over time, but explicit configuration is faster than waiting for it to infer your preferences.

Approve and merge

Evolve PRs go through your normal review process. You can add required reviewers, status checks, or any other branch protection rules you use for regular PRs.

If you want to add an automated approval gate for low-risk changes, you can configure Evolve to auto-merge PRs that meet specific criteria.

json

{
  "auto_merge": {
    "enabled": true,
    "conditions": {
      "rules": ["add-missing-error-handling"],
      "require_approvals": 0,
      "require_ci_pass": true,
      "max_files_changed": 3
    }
  }
}

With this configuration, add-missing-error-handling PRs that change three or fewer files auto-merge after CI passes. All other rules still require human approval.

Most teams start with auto-merge disabled and enable it selectively after they have reviewed a few runs of each rule and are confident in the quality.

Monitor improvements over time

The Autohand platform tracks metrics across every Evolve run. Check them via the CLI or the web dashboard.

bash

# View the last 30 days of Evolve activity
autohand evolve history --days 30

# Get a summary of merged improvements by rule
autohand evolve summary --repo github.com/your-org/your-repo

The summary output looks like this.

text

Evolve summary for your-org/your-repo (last 30 days)
------------------------------------------------------
Runs completed:          28
PRs created:             41
PRs merged:              34  (82.9% merge rate)
PRs closed (rejected):    7

By rule:
  extract-long-functions        18 created, 15 merged
  add-missing-error-handling    14 created, 14 merged
  consolidate-duplicate-utils    9 created,  5 merged

Files improved:         127
Avg PR size:            3.7 files changed

A high rejection rate on a specific rule is a signal to refine its criteria. Ask Autohand to help you tighten the rule based on your closed PRs.

bash

autohand "Look at the closed Evolve PRs for the consolidate-duplicate-utils rule
in the last 30 days. The rejection rate is high. What pattern do the rejected PRs share?
Suggest changes to the rule's match criteria in evolve.json to reduce false positives."

Customize evolution targets

Beyond the built-in improvement types, you can define custom targets for project-specific patterns.

Migrate a deprecated internal API to a new one.

json

{
  "name": "migrate-legacy-logger",
  "description": "Replace calls to the legacy logger.log() with the new structured logger",
  "enabled": true,
  "target": {
    "type": "pattern-match",
    "pattern": "logger.log(",
    "paths": ["src/"],
    "exclude_paths": ["src/lib/legacy-logger.js"]
  },
  "transformation": {
    "instruction": "Replace logger.log(message) with logger.info({ message }) using the structured logger imported from src/lib/logger.js. Match the log level to the context: errors should use logger.error, warnings should use logger.warn.",
    "max_files_per_pr": 10,
    "require_tests_pass": true
  }
}

Enforce a consistent response shape across all API handlers.

json

{
  "name": "standardize-api-responses",
  "description": "Ensure all route handlers return { data, error, meta } shaped responses",
  "enabled": true,
  "target": {
    "type": "inconsistent-pattern",
    "pattern": "route handlers not using the standard response shape",
    "paths": ["src/routes/"],
    "reference_file": "src/routes/users.js"
  },
  "transformation": {
    "instruction": "Update this route handler to return responses in the { data, error, meta } shape used in src/routes/users.js. The meta field should include at minimum the request id from req.id.",
    "max_files_per_pr": 5,
    "require_tests_pass": true
  }
}

Tip: Provide a reference_file for pattern-matching rules whenever you have a file that represents the ideal implementation. Evolve uses it as the transformation template, which produces much more consistent output than a purely textual instruction.

What you learned

  • You created an evolve.json configuration with rules targeting specific code improvement patterns
  • You registered a scheduled Evolve pipeline that runs on a cron schedule and creates pull requests
  • You configured auto-merge conditions for low-risk rules and reviewed generated proposals
  • You wrote custom evolution targets for project-specific migrations and consistency enforcement
Try next autohand "Review the last 5 Evolve PRs for this repository. Identify any patterns in the rejected ones and suggest improvements to evolve.json to reduce false positives."