BLOG/DEVELOPER DAILY
THEME
DEVELOPER DAILY

Scaffold a new service in one command

Stop copy-pasting last quarter's service and find-replacing the name. One OrchStep task prompts for a name and renders every file from templates — interactive locally, fully scriptable in CI.

Jun 21, 2026 OrchStep Team 6 minROLE: Backend DeveloperSCALE: Any
RUNNABLE DEMO
Full source for this post: blog/scaffold-service
VIEW SOURCE

Every team has a "reference service" that new services get cloned from. The ritual is always the same: copy the directory, find-and-replace old-service with new-service, delete the bits that don't apply, and forget to update two files so the new service ships with someone else's metadata. It works until it doesn't, and it's nobody's job to fix.

Scaffolding is a solved problem — you just need a template engine and a place to ask "what's it called?". OrchStep has both built in: func: render for Go-templated files, func: prompt for input that gracefully degrades to a default when nobody's at the keyboard.

The scaffold task

prompt asks for the name interactively; in CI it silently uses the default (or a --var). render turns a template plus that name into file contents:

orchstep.yml
name: scaffold
defaults:
  service: "widget"

tasks:
  # `orchstep run new`                            (prompts for the name)
  # `orchstep run new --var service_name=billing` (no prompt)
  new:
    desc: "Scaffold a new service from templates"
    steps:
      - name: service_name
        func: prompt
        args:
          message: "Service name"
          type: text
          default: "{{ vars.service }}"
        outputs:
          name: "{{ result.value }}"

      - name: main_go
        func: render
        args:
          template: |
            package main

            // {{ steps.service_name.name }} service
            func main() {
              println("{{ steps.service_name.name }} listening on :8080")
            }
        outputs:
          code: "{{ result.output }}"

      - name: dockerfile
        func: render
        do: "FROM golang:1.22 AS build  # {{ steps.service_name.name }}"
        outputs:
          content: "{{ result.output }}"

      - name: summary
        func: shell
        do: echo "scaffolded {{ steps.service_name.name }}/main.go and Dockerfile"

The demo keeps each rendered file in an outputs: value so it's safe to run anywhere. To actually write the files, add output_file: to a render step:

- name: main_go
  func: render
  args:
    template_file: "templates/main.go.tmpl"
    output_file: "services/{{ steps.service_name.name }}/main.go"

Note the path itself is templated — the service name decides where the file lands. That's the find-and-replace step you used to do by hand, done by the engine.

One workflow, human and robot

The prompt function is what makes this both an interactive tool and a CI primitive. At your terminal you get a real prompt with a default. In a pipeline — where stdin isn't a terminal — it skips the prompt and uses the default, or whatever you pass:

# Interactive: asks "Service name", offers the default
orchstep run new

# Scripted: no prompt, name supplied
orchstep run new --var service_name=billing

Same YAML, no if CI branch. The blog demo for testing made the case for "one command everywhere"; this is the same idea applied to a generator — the thing you run by hand on Friday is the thing your platform automation calls on Monday.

What you actually gained

ConcernCopy + find-replaceOrchStep new task
Naming the servicemanual search-replacepromptsteps.service_name.name
Where files landmove them by handtemplated output_file: path
Stale leftover metadataeasy to missrendered fresh from templates
Interactive vs scriptedtwo proceduresone task, prompt adapts
Lives intribal knowledgethe repo

Where OrchStep is not the answer

If you're scaffolding whole framework apps with deep conventions, a dedicated generator (Yeoman, cookiecutter, your framework's own CLI) will out-feature a handful of render steps. This pattern is the sweet spot for your house-style service — the five or ten files that are 90% boilerplate and 10% the name — without taking on a generator framework to produce them.

Where to go next

  • render — templating files, with template_file and output_file
  • prompt — interactive input that degrades to defaults in CI
  • Variables & Outputs — wiring prompt output into later steps
  • Quick Start — your first workflow in two minutes

Still cloning last quarter's service and find-replacing the name? Turn it into one orchstep run new. Install the binary and start with the demo above.

#SCAFFOLDING#TEMPLATES#RENDER#PRODUCTIVITY
Try it in two minutes — one binary, no signup.
curl -fsSL https://orchstep.dev/install.sh | sh

RELATED — DEVELOPER DAILY