Skip to main content

User-Defined Object Types

Modules can define their own object types. Use them when a workflow has a durable concept that deserves methods, documentation, and a stable contract.

Good object types represent concepts callers already understand:

  • A build target.
  • A deploy environment.
  • A release candidate.
  • A test suite.
  • A generated artifact.
  • A service bundle.

Objects should carry meaning. Do not create objects just to imitate classes in an SDK language.

When to introduce an object

Introduce an object when:

  • Several functions share the same context, and threading it through every call is repetitive.
  • The caller benefits from chaining related methods.
  • A returned value should expose more behavior than a single result.
  • The concept appears in traces, docs, or errors often enough to deserve a name.
  • Another module may want to consume the concept.

Keep a plain function when the workflow has one obvious input and one obvious output. An object that holds one field and exposes one method is just a function with extra ceremony.

Object names

Name objects after domain concepts, not implementation details. A caller should understand why an object exists before they know which SDK implemented it. The name is the first thing that shows up in help, traces, and generated clients — it should describe the concept, not the mechanism.

Implementation-shaped nameCaller-shaped nameWhy the second is better
BuildStateReleaseNames the concept the caller cares about, not the module's internal bookkeeping.
PythonContainerWrapperAppDrops the language and the "wrapper" mechanics; describes what it is.
TestRunnerContextTestSuiteA suite is a thing callers reason about; a "runner context" is an implementation detail.
DeployConfigStructEnvironmentNames the domain concept; "config struct" describes the code, not the workflow.
GeneratedFilesResultChangesetDescribes the reviewable outcome, not the fact that some files were produced.
KubeManifestBundleV2DeploymentVersion numbers and serialization formats are implementation noise.

The same rule applies to fields and methods: name them for what the caller wants, not for the intermediate values the module happens to hold.