Fiecare serviciu pe care îl scriu are nevoie de exact o informație înainte să facă orice altceva: chestia asta rulează în prod, sau vreun nenorocit (eu, la 2 noaptea, cu jumate de sticlă în mine) încă se joacă cu ea local. Asta e toată cerința. Ani la rând, răspunsul a stat sub forma os.Getenv("ENV") și a unui if de două linii, copiat în fiecare main.go pe care l-am scris vreodată, scris de fiecare dată puțin altfel. Așa că am făcut ce face orice inginer rezonabil când dă de o problemă de două linii: am scris un pachet întreg pentru ea, i-am pus un README cu secțiune de FAQ, i-am tăiat unsprezece release-uri și l-am livrat cu acoperire completă de teste. Ăsta e goenv.
Problema Care Nu Prea E o Problemă
Nu există niciuna, și ăsta e tot bancul. os.Getenv("ENV") are șaisprezece caractere. Să îl compari cu "dev" mai ia unsprezece. Ai putea scrie toată verificarea inline în mai puțin timp decât ți-a luat să citești propoziția asta. Numai că atunci ai avea-o inline, în fiecare serviciu, scrisă de fiecare dată puțin altfel, os.Getenv("ENVIRONMENT") aici, APP_ENV dincolo, cineva care verifică == "development" în loc de "dev" într-un al treilea repo, și dintr-odată logica ta de “sunt în prod?” e un câmp de mine împrăștiat prin toate serviciile tale, fiecare cablat greșit în felul lui special. goenv există ca întrebarea aia să aibă exact o implementare, definită exact într-un loc, importată peste tot unde contează.
Toată Implementarea
Uite toată implementarea. Nu un fragment, tot fișierul:
package goenv
import (
"os"
)
const EnvVarName = "ENV"
type Type = string
const (
Prod Type = "prod"
Dev Type = "dev"
)
func Get() Type {
e := os.Getenv(EnvVarName)
switch e {
case Dev:
return Dev
default:
return Prod
}
}
func IsProd() bool {
return Get() == Prod
}
func IsDev() bool {
return Get() == Dev
}Un singur import. Nici măcar fmt. Un switch cu un singur caz real și un default care aterizează mereu pe Prod. Pui ENV=dev și primești Dev. Îl pui pe orice altceva, prod, staging, test, string gol, o greșeală de tipar, sau nimic, și primești Prod. goenv nu are încredere că ai scris corect, deci nici nu încearcă să ghicească. Dacă nu e explicit dev, e prod. Fără cale de mijloc, fără prezumție de nevinovăție.
Suprafața exportată e exact trei funcții: Get() întoarce Type-ul curent (“prod” sau “dev”), iar IsProd() și IsDev() sunt cele două comodități booleene care doar cheamă Get() și compară. Trei constante le susțin, EnvVarName (“ENV”), Prod și Dev, plus un alias Type. Și vreau să fiu sincer despre ăla, pentru că e scris type Type = string, cu semnul egal, ceea ce îl face un alias adevărat, nu un tip definit. E interschimbabil cu string în ambele sensuri. Nu capeți absolut nicio siguranță la compilare din el, nimic nu te oprește să dai "banana" unde se așteaptă un Type. E documentație cu pași în plus, iar să pretind altceva ar fi exact genul de lucru de care pachetul ăsta există ca să râdă. Ăsta e tot API-ul. Fără struct de config, fără opțiuni funcționale, fără interfață de implementat, fără WithLogger(). Trei funcții, și deja le știi pe toate.
Cum Îl Folosești (Nu Prea Ai Ce Folosi)
go get github.com/psyb0t/goenvpackage main
import (
"fmt"
"github.com/psyb0t/goenv"
)
func main() {
if goenv.IsProd() {
fmt.Println("don't fuck this up")
}
if goenv.IsDev() {
fmt.Println("break whatever you want")
}
}Iar pe partea de deploy, e doar o variabilă de mediu ca oricare alta:
export ENV=dev # you're developing, go break shit
export ENV=prod # you're in production, don't
export ENV= # also production, because paranoia is a featureĂsta e exact trucul pe care îl folosește servicepack pentru propria detecție de mediu, același pachet, aceeași variabilă ENV, același default de “dacă nu zici explicit dev, ești în prod”. O dependență, un comportament, refolosit peste tot în loc să fie reinventat la fiecare serviciu.
Ce E de Fapt în Cutie
- Zero dependențe,
go.mode o linie de modul și o versiune de Go, nimic altceva. Singurul import din totgoenv.goeos. - Acoperire de teste reală, nu o lăudăroșenie din README, am tras sursa și am rulat singur
go test -cover ./...:100.0% of statements. Zece subteste în trei funcții de test acoperă fiecare ramură, dev, prod, string gol și o valoare nerecunoscută (“staging”) care și ea trebuie să cadă în prod, pentru că testele verifică chiar că regula “orice nu e explicit dev e prod” ține. - Cade pe prod, verificabil, scrie acolo în switch: singura cale care întoarce
Deve o potrivire exactă peENV=dev. Nesetat, gol, scris greșit, “staging”, “test”, orice, totul se rezolvă înProd. Pachetul nu are încredere în tine și nu se sfiește să o spună. - Unsprezece release-uri tăiate,
v1.0.0(“fuck yeah prod or dev”),v1.0.1(“peen”),v1.0.2, care a adăugat un skill de agent pe ClawHub, și încă opt care nu au schimbat absolut niciun rând de bibliotecă. Da. Un pachet cu trei funcții care citește o variabilă de mediu livrează acum un skill de agent documentat, ca să i se poată explica unui AI cum să chemeIsProd(). Fișierul de skill e mai lung decât biblioteca pe care o documentează. Pe urmă au venit badge-urile, notele de licență, un mirror pe Codeberg, o împărțire a pipeline-ului de CI, Go 1.26, și un release al cărui changelog întreg e că rezumatul de suprafață din README uitase să pomeneascăType.goenv.goare exact un commit în toată istoria lui, ăla inițial, deci fiecare release de după primul e împachetare, hârțogărie sau corectură de typo, ceea ce e cumva și cel mai amuzant, și cel mai corect deznodământ disponibil. Toate unsprezece reale, toate unsprezece trecute prin același pipeline de CI ca orice alt pachet pe care îl livrez:go vet,go test -raceși un prag de acoperire setat la minim 90% în Makefile, iar de la v1.0.2 și un job de publicare legat de tag, care trimite skillul pe ClawHub după ce trec lintul și testele, un prag pe care pachetul ăsta îl depășește cu zece puncte întregi fără să facă mai nimic.
Pe Scurt
Dacă îți trebuie mai mult de un bit de informație din mediu, tipuri, valori implicite, structuri, tot tacâmul, du-te și citește despre gonfiguration, aia e versiunea de om mare a ideii ăsteia. goenv e la celălalt capăt al spectrului: răspunde la o întrebare, răspunde la fel de fiecare dată, și nu se preface că e mai mult de atât.
Ia-l de pe GitHub, sau scrie-ți singur os.Getenv("ENV") == "dev" ca un om normal. Merge și așa, și așa. Nu sunt șeful tău.
Cum Îl Instalezi în Agentul Tău
Da, pachetul de trei funcții are plugin. Nu, nu o să mai vorbesc despre asta. Tot ce e sub .agents/ e catalogat într-un singur marketplace, deci sunt două comenzi:
claude plugin marketplace add psyb0t/agents
claude plugin install goenv@psyb0tCodex folosește același marketplace cu alt verb, codex plugin add goenv@psyb0t, pentru că nu există codex plugin install. Găsește singur și skillul într-un checkout al repo-ului, pentru că scanează .agents/skills/ nativ, fără să fie instalat absolut nimic.