Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:
exit 1
fi

- name: Check Generated Stubs
run: make check-generate

- name: Test (Production Mode)
run: make test

Expand Down
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ TAGS_DEBUG := -tags debug
GENERATOR_CMD := cmd/gen-release/main.go
GENERATOR_BIN := gen-release

.PHONY: all test test-debug fmt tidy generate build-generator help
.PHONY: all test test-debug fmt tidy generate check-generate build-generator help

all: test ## Run all tests (release mode)

Expand All @@ -26,6 +26,14 @@ generate: build-generator ## Generate release stubs from debug code by running g
@echo "Generating release stubs..."
./$(GENERATOR_BIN) -in bumert_debug.go

check-generate: generate ## Fail if committed release stubs differ from freshly generated ones
@echo "Checking generated release stubs are up to date..."
@if ! git diff --quiet bumert_release.go; then \
echo "Release stubs are out of date. Run 'make generate' and commit the result."; \
git diff bumert_release.go; \
exit 1; \
fi

test: generate ## Run tests without debug assertions (generates stubs first)
@echo "Running tests (release mode)..."
$(GOTEST) $(PKG)
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ To **enable** assertions, you must include the `debug` build tag:

**Running/Testing without the tag** (e.g., `go test ./...` or `make test` or `go build main.go`) produces a binary where all `bumert` checks are **completely absent**.

## Important: the check is free, the argument is not

The "zero overhead" guarantee covers the assertion machinery itself. In a release build the calls are inlined away to nothing, including the interface boxing, so a check like `Should(ptr).NotBeNil()` compiles down to zero instructions and performs no allocations.

What it does **not** do is remove the expression you pass in. Go evaluates function arguments before the call, and a no-op cannot un-evaluate them. This is different from a C-style `assert()` macro, which the preprocessor deletes entirely under `NDEBUG`. So with `bumert` the value being asserted is still computed in production:

```go
// The pointer load is free and the whole line disappears in release builds.
bumert.Should(ptr).NotBeNil()

// expensiveCheck() STILL RUNS in release builds, even though the assertion
// itself is a no-op. Only the assertion around the result is removed.
bumert.Assertf(expensiveCheck(), "must hold")
```

If the thing you want to assert is expensive or has side effects, defer it behind a closure with `TrueFn`. The closure is never called in release builds, so the work is skipped there:

```go
cfg := loadConfig()
bumert.Should(cfg).TrueFn(func() bool {
return cfg.IsValid() // expensive; only runs with -tags debug
})
```

Rule of thumb: pass plain values and pointers directly, and put anything costly behind `TrueFn`.

## Panic Behavior

When the `debug` tag is enabled, a **failed assertion will cause a panic**. This is intentional and follows the common pattern for assertion libraries in Go (and other languages). It immediately halts execution at the point of the violated assumption during development or debugging, making issues easy to spot. The panic message includes the file and line number of the failed assertion, along with the failure reason (e.g., "Expected <foo> to be greater than <bar>").
Expand Down
Loading