# How Dewy Handles Deployment Status Notifications: A Deep Dive into the Notifier Subsystem

> Explore how Dewy manages deployment status notifications via a pluggable interface supporting Slack and email. Learn about error throttling to prevent spam.

- Repository: [Tomohisa Oda/dewy](https://github.com/linyows/dewy)
- Tags: deep-dive
- Published: 2026-03-06

---

**Dewy routes deployment status notifications through a pluggable notifier interface that supports Slack, email, and null implementations, with built-in error throttling via `ErrorLimitingSender` to prevent notification spam.**

The `linyows/dewy` deployment tool provides real-time feedback throughout the deployment lifecycle using a dedicated notifier subsystem. When Dewy performs a deployment, it reports every stage—from initialization through hook execution to final success or failure—via configurable channels. Understanding how Dewy handles deployment status notifications helps operators monitor infrastructure changes and debug failures without noise.

## The Notification Lifecycle in Dewy

Dewy’s notification flow follows the deployment lifecycle sequentially, with specific methods called at each stage.

### Notifier Initialization

Before any deployment begins, Dewy constructs the notifier in [`dewy.go`](https://github.com/linyows/dewy/blob/main/dewy.go):

```go
// dewy.go – New() (excerpt)
d.notifier, err = notifier.New(ctx, d.config.Notifier, d.logger.Logger)

```

The `notifier.New` factory function parses the configuration URL (e.g., `slack://my-channel?quiet=true` or `mail://...`) and returns a concrete implementation wrapped by `ErrorLimitingSender`. This wrapper adds error-count limiting and quiet mode handling regardless of the underlying transport.

Source: [[`notifier/notifier.go`](https://github.com/linyows/dewy/blob/main/notifier/notifier.go)](https://github.com/linyows/dewy/blob/main/notifier/notifier.go)

### Deployment Start Notification

When Dewy begins execution, it immediately notifies the configured channel:

```go
msg := fmt.Sprintf(
    "Automatic shipping started by *Dewy* (v%s: %s)",
    d.config.Version, d.config.Command.String(),
)
d.logger.Info("Dewy start notification", slog.String("message", msg))
d.notifier.Send(ctx, msg)

```

Source: [[`dewy.go`](https://github.com/linyows/dewy/blob/main/dewy.go) line 52‑55](https://github.com/linyows/dewy/blob/main/dewy.go#L52-L55)

### Pre-Deploy Hook Results

Before extracting the archive, Dewy executes the configured `BeforeDeployHook` and reports the result:

```go
beforeResult, beforeErr := d.execHook(d.config.BeforeDeployHook)
if beforeResult != nil {
    d.notifier.SendHookResult(ctx, "Before Deploy", beforeResult)
}

```

Source: [[`dewy.go`](https://github.com/linyows/dewy/blob/main/dewy.go) line 10‑13](https://github.com/linyows/dewy/blob/main/dewy.go#L10-L13)

The `SendHookResult` method creates a detailed attachment containing stdout, stderr, exit code, and duration. In the Slack implementation, this renders as a rich message block via `BuildHookAttachment`.

Source: [[`slack.go`](https://github.com/linyows/dewy/blob/main/slack.go) → `BuildHookAttachment`](https://github.com/linyows/dewy/blob/main/notifier/slack.go#L44-L98)

### Deployment Execution and Error Handling

If the deployment fails during archive extraction or symlink switching, Dewy captures the error:

```go
if err != nil {
    d.logger.Error("Preserve failure", slog.String("error", err.Error()))
    d.notifier.SendError(ctx, err)
    return err
}

```

The `ErrorLimitingSender.SendError` implementation tracks the error count, limiting noisy notifications to a maximum of three messages while still logging all errors internally.

Source: [[`notifier/notifier.go`](https://github.com/linyows/dewy/blob/main/notifier/notifier.go) line 79‑90](https://github.com/linyows/dewy/blob/main/notifier/notifier.go#L79-L90)

### Post-Deploy Hook Results

After successful deployment, a deferred function executes the `AfterDeployHook` and reports its result:

```go
defer func() {
    if err != nil { return }
    afterResult, afterErr := d.execHook(d.config.AfterDeployHook)
    if afterResult != nil {
        d.notifier.SendHookResult(ctx, "After Deploy", afterResult)
    }
    // …
}()

```

Source: [[`dewy.go`](https://github.com/linyows/dewy/blob/main/dewy.go) line 23‑27](https://github.com/linyows/dewy/blob/main/dewy.go#L23-L27)

## Error Throttling and Quiet Mode

Dewy prevents notification spam through the `ErrorLimitingSender` wrapper. This component maintains an `errorCount` and a `quiet` flag parsed from the notifier URL (e.g., `?quiet=true`).

- **`Send`** respects both quiet mode and the error count limit.
- **`SendImportant`** bypasses quiet mode but still respects the error count.
- **`maxNotifyErrors`** is set to **3**; after three error notifications, a final "quiet" warning is sent and subsequent errors are silenced until `ResetErrorCount` is called (typically after a successful deployment).

Source: [[`notifier/notifier.go`](https://github.com/linyows/dewy/blob/main/notifier/notifier.go) line 59‑104](https://github.com/linyows/dewy/blob/main/notifier/notifier.go#L59-L104)

## Supported Notification Channels

Dewy’s notifier interface allows swapping delivery mechanisms without changing the core deployment logic.

### Slack Integration

The Slack implementation formats messages with rich attachments for hook results, including color-coded status (green for success, red for failure), command output, and execution duration. Configure via `slack://channel-name?token=xoxb-...&quiet=true`.

Source: [[`notifier/slack.go`](https://github.com/linyows/dewy/blob/main/notifier/slack.go)](https://github.com/linyows/dewy/blob/main/notifier/slack.go)

### Email Notifications

The mail sender delivers plain-text notifications suitable for traditional monitoring setups. Configure via `mail://smtp.example.com:587?from=...&to=...`.

Source: [[`notifier/mail.go`](https://github.com/linyows/dewy/blob/main/notifier/mail.go)](https://github.com/linyows/dewy/blob/main/notifier/mail.go)

### Null Notifier (Disabled Mode)

When no notifier URL is provided or explicitly set to `null://`, Dewy uses the null implementation which silently discards all notifications. This is useful for local testing or when logging alone is sufficient.

Source: [[`notifier/null.go`](https://github.com/linyows/dewy/blob/main/notifier/null.go)](https://github.com/linyows/dewy/blob/main/notifier/null.go)

## Implementation Example

Here is a complete example demonstrating how to instantiate and use the notifier outside of Dewy’s main loop:

```go
ctx   := context.Background()
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

// Create a Slack notifier (quiet mode enabled)
notif, err := notifier.New(ctx, "slack://my-channel?quiet=true", logger)
if err != nil { log.Fatal(err) }

// Deploy start
notif.Send(ctx, "🚀 Starting deployment of v1.2.3")

// Simulate a hook result
hook := &notifier.HookResult{
    Command:  "make build",
    Stdout:   "build succeeded",
    Stderr:   "",
    ExitCode: 0,
    Duration: 2 * time.Second,
    Success:  true,
}
notif.SendHookResult(ctx, "Before Deploy", hook)

// Simulate an error
notif.SendError(ctx, fmt.Errorf("network timeout"))
// Subsequent errors will be throttled after 3 occurrences.

```

## Summary

- **Pluggable Architecture**: Dewy uses a `Notifier` interface with concrete implementations for Slack, email, and null (disabled) channels, configured via URL schemes like `slack://` or `mail://`.
- **Lifecycle Coverage**: Notifications are sent at every stage: deployment start, pre-deploy hook results, deployment errors, and post-deploy hook results via methods like `Send`, `SendHookResult`, and `SendError`.
- **Spam Prevention**: The `ErrorLimitingSender` wrapper throttles error notifications to a maximum of three messages and supports a `quiet` mode to suppress non-critical updates.
- **Rich Context**: Hook results include detailed attachments showing stdout, stderr, exit codes, and duration, formatted appropriately for each channel (e.g., Slack rich attachments).

## Frequently Asked Questions

### How do I configure Slack notifications in Dewy?

Configure Slack by setting the notifier URL to `slack://channel-name?token=xoxb-your-token`. You can append `&quiet=true` to suppress non-error messages. The Slack implementation automatically formats hook results as rich attachments with color-coded status indicators.

### What happens when deployment errors occur repeatedly?

Dewy’s `ErrorLimitingSender` tracks error counts and limits notifications to the first three errors. After the third error, a final "quiet mode activated" message is sent, and subsequent errors are logged but not sent to the notification channel until a successful deployment resets the counter.

### Can I disable notifications entirely in Dewy?

Yes. Set the notifier URL to `null://` or omit the notifier configuration entirely. This activates the `Null` implementation, which implements the `Notifier` interface but silently discards all messages, effectively disabling notifications while maintaining code compatibility.

### How does Dewy format hook execution results for Slack?

The `SendHookResult` method creates a `HookResult` struct containing the command, stdout, stderr, exit code, duration, and success status. The Slack implementation in [`slack.go`](https://github.com/linyows/dewy/blob/main/slack.go) converts this into a rich attachment with a color bar (green for success, red for failure), formatted fields for each output stream, and a timestamp, providing immediate visual context for hook failures.