Skip to content
This repository was archived by the owner on Oct 12, 2026. It is now read-only.

refactor: make the validators an importable Go library - #10

Merged
jeferson0306 merged 1 commit into
masterfrom
feat/go-library
Sep 4, 2026
Merged

jeferson0306 merged 1 commit into
masterfrom
feat/go-library

Conversation

@jeferson0306

Copy link
Copy Markdown
Collaborator
go get github.com/jeferson0306/api-data-validator/validate
result := validate.CPF("529.982.247-25")
result.Valid      // true
result.Normalized // "52998224725"

The logic was trapped inside an HTTP service. Nobody could go get it, the module path (DataValidatorAPI) was not even a valid import path, and importing the package would have opened a Redis connection.

What makes it a library

The library is pure. No network, no cache, no configuration, no state, nothing read from the environment. Validation is arithmetic, and arithmetic that needs a server is arithmetic you cannot trust when the server is down.

The Redis client left the library for internal/cache. It was a package-level variable, so importing a CPF validator dialled a database — the single clearest sign this was not a library.

Functions return a Result rather than three unnamed values. (bool, string, string) is fine inside a package and hostile across one.

The package is named for what it provides. validate.CPF reads better than utils.ValidateCPF, and Go style avoids the stutter.

The benchmarks change an argument

BenchmarkCPF-12                  522 ns/op
BenchmarkCNPJ-12                 499 ns/op
BenchmarkBoleto-12               864 ns/op
BenchmarkStateRegistration-12    248 ns/op

A CPF check costs 522 nanoseconds. The Redis round trip measured in production was 150–220 milliseconds — five orders of magnitude apart. Worse, building the cache key requires the normalised value, and normalising is most of the validation, so the answer is already computed before the cache is consulted.

The cache is retained because from_cache is part of the published response, but the numbers argue for removing both. Your call — I did not want to drop a published field without asking.

This is also a production fix. The cache is now opt-in through REDIS_ADDR. With none configured — the current state on Render — the service was attempting a connection and timing out on every CPF request. It now skips it. /health gained a third state to say so:

{"cache":"disabled","status":"ok"}

Command line

go install github.com/jeferson0306/api-data-validator/cmd/brdoc@latest

brdoc cpf 529.982.247-25 && echo accepted
brdoc ie 0100482300112 AC
brdoc --json cnpj 33.000.167/0001-01 | jq .normalized

Exit 0 for valid, 1 for invalid, 2 for usage — so it composes with a shell rather than needing to be parsed.

Documentation

Seven runnable examples, which are both the documentation on pkg.go.dev and tests that fail if the behaviour drifts. They cover the things worth knowing before using it: that stray characters are rejected rather than stripped, that a rejected value comes back untouched, and that ie needs its state.

The deployment is untouched

main.go stays at the repository root, so the existing Render build command still finds it. Smoke tested end to end against a running binary: every endpoint answers correctly, the batch works, and the log still contains no trace of the value being validated.

Follow-ups

The repository is named api-data-validator, which makes the import path read oddly for a library. Renaming it — GitHub redirects the old URL — would give github.com/jeferson0306/brdoc/validate. Worth doing before anyone depends on the current path, but it is a decision, not a cleanup.

The logic was trapped inside an HTTP service. Nobody could go get it, the
module path was not even a valid import path, and importing the package would
have opened a Redis connection.

  go get github.com/jeferson0306/api-data-validator/validate

  result := validate.CPF("529.982.247-25")
  result.Valid      // true
  result.Normalized // "52998224725"

The library is pure. No network, no cache, no configuration, no state, nothing
read from the environment. Validation is arithmetic, and arithmetic that needs
a server is arithmetic you cannot trust when the server is down.

Changes that make it one:

The module path is now importable, and the package is named for what it
provides rather than for where it happened to live. validate.CPF reads better
than utils.ValidateCPF, and Go style avoids the stutter.

Functions return a Result rather than three unnamed values. (bool, string,
string) is fine inside a package and hostile across one.

The Redis client left the library for internal/cache. It was a package-level
variable, so importing a CPF validator dialled a database.

The cache is now opt-in through REDIS_ADDR and off by default, which is also a
production fix: with no Redis configured the service was attempting a
connection and timing out on every CPF. /health gained a third state to say so.

Benchmarks were added, and they change the argument about the cache. A CPF
check runs in 522ns against a Redis round trip measured at 150-220ms in
production — five orders of magnitude apart. Building the cache key requires
normalising, and normalising is most of the validation, so the answer is
already computed before the cache is consulted. from_cache is kept because it
is published, but the numbers argue for removing both.

cmd/brdoc is a command line tool over the same library, exiting 0 for valid and
1 for invalid so it composes with a shell.

Seven runnable examples serve as documentation on pkg.go.dev and as tests.

main.go stays at the repository root so the existing deployment is untouched.
Smoke tested end to end: every endpoint answers, and the log still contains no
trace of the value being validated.
@jeferson0306
jeferson0306 merged commit 2784d25 into master Sep 4, 2026
1 check passed
@jeferson0306
jeferson0306 deleted the feat/go-library branch September 4, 2026 09:22
jeferson0306 added a commit that referenced this pull request Sep 4, 2026
Three changes that belong together because each is a break, and one break is
cheaper than three.

The module is github.com/jeferson0306/brdoc. api-data-validator described a
service; what this mostly is now is a library, and .../api-data-validator/validate
reads badly in an import block. GitHub redirects the old URL, and nothing
depends on the old module path yet — which is precisely why this is the moment.

The cache is gone. #10's benchmarks put a CPF check at 522ns against a Redis
round trip measured at 150-220ms in production, and building the cache key
required the normalised value, which is most of the validation — the answer was
already computed before the cache was consulted. It was never an optimisation.
from_cache leaves the response with it, and /health stops reporting a subsystem
that no longer exists: validation is arithmetic with no dependencies, so if the
process answers, it works. Enumerating subsystems a service does not have is
theatre.

/v1 is mounted, and the unversioned paths stay permanently. They were
published, things point at them, and breaking a URL to tidy a prefix is a cost
paid by other people. /v1 exists so the next change has somewhere to go rather
than to move anyone today.

Smoke tested against a running binary: both route generations answer, from_cache
is absent from the response, and health reports one field.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant