📎 Пусть main делает реальную работу — 8 июля 2026 г. в 10:13:00.987
📎 Пусть main делает реальную работу «Просто свяжи всё в main, логику вынеси в пакеты, держи main.go в двадцать строк». Так советуют. Совет не то чтобы неверный, но ведёт к конкретной поломке. Вы получаете папку cmd/ из пакетов, которые вызываются ровно из одного места, и main.go на двадцать строк вида «вызвать функцию бутстрапа в internal/app». Вы добавили лишний слой, но не добавили абстракции. ➡️ Что делать вместо Пусть main делает настоящую работу. Читает конфиг, инициализирует пул базы, связывает зависимости, поднимает gRPC сервер и регистрирует обработчик остановки. Если ваш main.go на двести строк, но каждая строка это честная проводка без бизнес логики и без условий по фичефлагам, это нормально: func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() cfg := config.MustLoad() // паникует на плохом конфиге, намеренно на старте pool, err := pgxpool.New(ctx, cfg.DatabaseURL) if err != nil { log.Fatal("failed to initialize database pool", zap.Error(err)) } defer pool.Close() queries := db.New(pool) memberSvc := members.NewService(queries) srv := grpc.NewServer(grpc.ChainUnaryInterceptor( logging.UnaryServerInterceptor(logger), recovery.UnaryServerInterceptor(), )) memberspb.RegisterMembersServiceServer(srv, memberSvc) go func() { if err := srv.Serve(lis); err != nil { log.Fatal("server exited", zap.Error(err)) } }() <-ctx.Done() srv.GracefulStop() } Это читаемо, и это реальная топология приложения, видная в одном файле. Тонкий main оправдан, только когда стартовая логика действительно сложная и заслуживает отдельного пакета. В остальных случаях честная проводка прямо в main понятнее.

