Dave Rolsky

Like Make
(and Rake and Cake and ...)

  • Declarative dependencies
  • Ask for a final outcome, it figures out how to get there
  • Knows how to skip steps which are up to date

Totally Unlike Make
(and Rake and Cake and ...)

  • Each step is a unique Perl class
  • Dependencies are class attributes
  • Productions are also class attributes
  • No DSL

Running Stepford

use Stepford::Runner;

my $runner = Stepford::Runner->new(
    step_namespaces => 'My::Step::Prefix',
    # optional
    logger          => Log::Dispatch->new(...),
    jobs            => 4,

    final_steps => 'My::Step::Prefix::DeployDatabase',
    # optional
    config      => {
        output_dir => '...',

Dependencies and Productions

  • These are just regular Moose attributes
  • Can be any type of data (but should be serializable)
  • Dependency resolution matches attribute names

Step Classes

  • Can declare productions, dependencies, or both
  • A class can have multiple productions
  • Steps must consume the Stepford::Role::Step role
    • or Stepford::Role::Step::FileGenerator

Step Classes Continued

  • Classes must define run() and last_run_time()
    • FileGenerator role provides last_run_time()
  • The run() should do all heavy lifting, not builders!

Simple Step Class

package Thing::Step::WriteDataFile;
use File::Slurp::Tiny qw( write_file );
use Moose;
with 'Stepford::Role::Step::FileGenerator';

has output_dir => (
    traits   => ['StepDependency'],
    is       => 'ro',
    isa      => 'Dir',
    required => 1,

has data_file => (
    traits  => ['StepProduction'],
    is      => 'ro',
    isa     => 'File',
    lazy    => 1,
    default => sub { $_[0]->output_dir()->file('data') },

sub run {
    my $self = shift;
    write_file( $self->data_file, 'foo' );

Creating a Stepford::Runner

  1. Loads all your step classes
  2. Doesn't do anything else until you call run()


  1. Calculates the dependency tree, throwing errors if ...
    • Two steps have productions of the same name
    • It finds a cycle in the dependencies
    • A step has a dependency that cannot be satisfied by an other step's productions
  2. Calculates a concrete plan for the given final steps
  3. Runs the plan by ...
    1. Getting the next step class to run
    2. Creating an object for the class
      • Parameters for the class can come from arguments passed to run() as well as relevant productions from previous steps
    3. Checking if a step is up to date and running it if needed
      • Running may happen in a child process

All Done