Writing specifications for coding agents
A compact structure for specifying behaviour, preserving implementation discretion and recording the decisions that remain open.
A compact structure for specifying behaviour, preserving implementation discretion and recording the decisions that remain open.
Follow a bounded invoice-export investigation from hidden decisions through agent evidence, separate specification failures and a local trial of the practices involved.
How to check that an agent can use a specification, review bounded implementation evidence, and reconcile faulty or changed requirements with active work.
How BDD can expose disagreements, how acceptance evidence earns its authority, and when to implement, investigate or revise a specification.
Choose discovery, investigation or a bounded implementation before writing the next contract for a coding agent.
Lifecycle management ensures that application components like servers, databases, and background workers are correctly initialized, started in the right order, and gracefully stopped.
Go developers typically wire dependencies manually, instead of relying on heavy frameworks or reflection based dependency injection. This approach improves readability and reduces hidden behavior, but it also shifts the burden of managing component interactions and lifecycle sequencing onto the developer.
As applications scale, the lack of built-in orchestration increases the risk of subtle bugs and inconsistent shutdown behavior.
Event logging is one of the three pillars of observability (traces and metrics being the other two).
A log entry is a timestamped record about the application’s activity, which can be used for troubleshooting, monitoring or auditing purposes.
Logs may have different formats:
This article is a non-exhaustive walkthrough of logging in Go.
Opentelemetry provides vendor neutral standards and implementations to generate, collect, and export tracing data.
A Context is a propagation mechanism which carries execution-scoped values across API boundaries and between logically associated execution units. Cross-cutting concerns access their data in-process using the same shared Context object.
Context propagation is required when the tracing needs to cross process or service boundaries. Common ways to propagate the context is using W3C Trace Context or Zipkin B3 headers, while to inject the context is, for example, http headers or metadata fields in event messages.
Go Type Parameters Proposal is expected to be implemented with Go 1.18 early 2022.
The Very high level overview section contains a nice overview of what to expect.
If you have seen generics in other languages perhaps the most curious differences (other than using square brackets), are union types:
type SignedIntegers interface {
int | int8 | int16 | int32 | int64
}
Similar in syntax to scala3/dotty union types, although in Go is only allowed in constraints. The type set of this union element is the set {int, int8, int16, int32, int64}.
Doing code reviews, I have sometimes noticed libraries forcing dependencies on the users.
While it is perfectly acceptable if well justified and documented, it sometimes feels like adding unnecessary baggage.
A typical example would be something like the following snippet:
func (c *Client) HealthCheck(ctx context.Context) error {
logrus.Debug("sending get request")
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url, nil)
if err != nil {
return err
}
resp, err := c.client.Do(req)
if err != nil {
return err
}
logrus.Debugf("http status code was %d", resp.StatusCode)
defer resp.Body.Close()
return nil
}
Using logrus, as example, picking this code, one would need to check how logrus works, which configurations are available, which version should be used and so on.