Open Source Tools We Use Daily at Our Agency
Open Source Tools We Use Daily at Our Agency
我们在代理机构日常使用的开源工具
Agency work has a way of stress-testing your tools. A stack that feels fine on one internal project can become painful when you are supporting multiple clients, different hosting environments, handoffs, tight timelines, and production incidents that do not wait for the calendar to clear. 代理机构的工作方式往往会给你的工具带来压力测试。一套在某个内部项目中感觉良好的技术栈,在支持多个客户、不同的托管环境、工作交接、紧迫的时间节点以及不等人处理的生产事故时,可能会变得非常痛苦。
At Paradane, we build custom software, e-commerce systems, SaaS products, API integrations, and automation workflows. That means our day-to-day work moves between frontend interfaces, backend services, databases, deployment pipelines, and debugging sessions. The tools that survive are usually not the flashiest ones. They are the ones that make problems visible, keep teams aligned, and reduce the number of things we have to remember under pressure. Here are open source tools we reach for constantly, plus where each one fits in real client work. 在 Paradane,我们构建定制软件、电子商务系统、SaaS 产品、API 集成和自动化工作流。这意味着我们的日常工作需要在前端界面、后端服务、数据库、部署流水线和调试会话之间切换。能够留存下来的工具通常不是最花哨的,而是那些能让问题可视化、保持团队步调一致,并减少我们在压力下需要记忆的事项的工具。以下是我们经常使用的开源工具,以及它们在实际客户项目中的应用场景。
1. PostgreSQL: boring in the best way
1. PostgreSQL:以最好的方式保持“无聊”
PostgreSQL is still our default relational database for most custom applications. It gives us transactions, constraints, indexes, JSON support, full-text search, row-level security, and mature operational behavior in one package. The biggest reason we like it is not just performance. It is the way PostgreSQL helps encode business rules close to the data. Foreign keys, unique constraints, check constraints, and transactional migrations prevent a lot of bugs from becoming permanent data problems. PostgreSQL 仍然是我们大多数定制应用的首选关系型数据库。它在一个包中为我们提供了事务、约束、索引、JSON 支持、全文搜索、行级安全性和成熟的操作行为。我们喜欢它的最大原因不仅仅是性能,而是 PostgreSQL 能够帮助我们将业务规则编码在靠近数据的地方。外键、唯一约束、检查约束和事务性迁移可以防止许多 Bug 演变成永久性的数据问题。
A simple example: if an e-commerce workflow requires one active cart per customer, that should not live only in application code. A partial unique index can enforce it: CREATE UNIQUE INDEX one_active_cart_per_customer ON carts (customer_id) WHERE status = 'active'; That kind of guardrail saves time when there are multiple backend jobs, webhooks, admin actions, and checkout paths touching the same data.
一个简单的例子:如果电子商务工作流要求每个客户只能有一个活跃购物车,这不应该仅仅存在于应用代码中。一个部分唯一索引(Partial Unique Index)可以强制执行它:CREATE UNIQUE INDEX one_active_cart_per_customer ON carts (customer_id) WHERE status = 'active'; 当有多个后端作业、Webhook、管理操作和结账路径同时触及相同数据时,这种护栏可以节省大量时间。
2. Redis: small cache, big leverage
2. Redis:小缓存,大杠杆
Redis is one of those tools that can be misused easily, but when scoped well it is extremely effective. We use it for short-lived cache entries, rate limiting, background job coordination, session-like data, and computed values that are expensive but safe to rebuild. A pattern we like is to make cache keys explicit and versioned: const key = v2:product:${productId}:pricing:${currency}; That sounds minor, but clear key naming makes debugging much easier. When a client says a price, dashboard number, or inventory value looks wrong, you can inspect exactly which computed value is being served and invalidate it without flushing unrelated data.
Redis 是那种很容易被误用,但如果范围界定得当就极其有效的工具。我们将其用于短生命周期的缓存条目、速率限制、后台作业协调、类似会话的数据,以及那些计算成本高但可以安全重建的计算值。我们喜欢的一种模式是使缓存键明确且版本化:const key = v2:product:${productId}:pricing:${currency}; 这听起来微不足道,但清晰的键命名使调试变得容易得多。当客户说价格、仪表板数字或库存值看起来不对时,你可以准确检查正在提供哪个计算值,并在不刷新无关数据的情况下使其失效。
3. Playwright: testing the actual user path
3. Playwright:测试真实的用户路径
Unit tests are valuable, but a lot of business-critical defects happen at the seams: checkout flows, login redirects, form validation, third-party scripts, file uploads, and browser-specific behavior. Playwright gives us a reliable way to test those flows. We especially like using Playwright for smoke tests after deployment. A smoke test does not need to assert everything. It needs to answer: can a real user still do the most important thing? 单元测试很有价值,但许多业务关键缺陷发生在衔接处:结账流程、登录重定向、表单验证、第三方脚本、文件上传和特定于浏览器的行为。Playwright 为我们提供了一种测试这些流程的可靠方法。我们特别喜欢在部署后使用 Playwright 进行冒烟测试。冒烟测试不需要断言所有内容,它只需要回答:真实用户是否仍然可以完成最重要的事情?
await page.goto(process.env.APP_URL!); await page.getByRole('link', { name: /pricing/i }).click(); await page.getByRole('button', { name: /start/i }).click(); await expect(page.getByText(/create your account/i)).toBeVisible(); That one check can catch broken routes, missing environment variables, failed builds, and bad redirects before a client reports them.
await page.goto(process.env.APP_URL!); await page.getByRole('link', { name: /pricing/i }).click(); await page.getByRole('button', { name: /start/i }).click(); await expect(page.getByText(/create your account/i)).toBeVisible(); 这一项检查可以在客户报告之前,捕获损坏的路由、缺失的环境变量、构建失败和错误的重定向。
4. Docker Compose: repeatable local environments
4. Docker Compose:可重复的本地环境
For many client projects, Docker Compose is still the fastest path to a repeatable development environment. A new developer should not need a 12-step setup document to run the app. They should clone the repo, copy an example environment file, and start the core services. A typical local stack might include: web app, API service, PostgreSQL, Redis, local mail catcher, object storage emulator. The goal is not to containerize everything forever. The goal is to remove ambiguity. If everyone is running the same database version, queue, and service ports locally, onboarding and debugging get simpler. 对于许多客户项目,Docker Compose 仍然是实现可重复开发环境的最快途径。新开发者不应该需要一份 12 步的设置文档才能运行应用。他们应该克隆仓库、复制示例环境文件并启动核心服务。典型的本地技术栈可能包括:Web 应用、API 服务、PostgreSQL、Redis、本地邮件捕获器、对象存储模拟器。目标不是永远容器化一切,而是消除歧义。如果每个人在本地运行相同的数据库版本、队列和服务端口,入职和调试就会变得更简单。
5. Nginx and Caddy: simple edge routing
5. Nginx 和 Caddy:简单的边缘路由
We see both Nginx and Caddy in production setups. Nginx is battle-tested and extremely flexible. Caddy shines when you want automatic HTTPS and a cleaner configuration for straightforward apps. For smaller deployments, Caddy can be refreshingly concise: app.example.com { reverse_proxy localhost:3000 } For more complex routing, caching, and legacy constraints, Nginx is often the better fit. The important thing is to keep edge configuration documented and version-controlled. Reverse proxies are too important to live only in someone’s memory.
我们在生产环境中同时使用 Nginx 和 Caddy。Nginx 经过了实战检验且极其灵活。当你需要自动 HTTPS 和针对简单应用的更简洁配置时,Caddy 则表现出色。对于较小的部署,Caddy 可以做到令人耳目一新的简洁:app.example.com { reverse_proxy localhost:3000 } 对于更复杂的路由、缓存和遗留约束,Nginx 通常更合适。重要的是要保持边缘配置的文档化和版本控制。反向代理太重要了,不能只存在于某人的记忆中。
6. GitHub Actions: useful when workflows stay focused
6. GitHub Actions:当工作流保持专注时非常有用
CI/CD pipelines become frustrating when they try to do too much. We prefer small workflows with clear ownership: lint and type check, run tests, build artifact, deploy to staging, deploy to production after approval. A good pipeline should fail loudly and specifically. If the failure message makes the next action obvious, the workflow is doing its job. We also like adding scheduled checks for things clients rarely think about until they break: expired certificates, broken links, failed cron jobs, stale dependencies, and API health checks. 当 CI/CD 流水线试图做太多事情时,它们会变得令人沮丧。我们更喜欢职责明确的小型工作流:Lint 和类型检查、运行测试、构建制品、部署到预发布环境、审批后部署到生产环境。一个好的流水线应该响亮且具体地报错。如果失败信息让下一步行动显而易见,那么这个工作流就完成了它的使命。我们还喜欢为客户在崩溃前很少考虑的事情添加定时检查:过期的证书、损坏的链接、失败的 Cron 作业、过时的依赖项以及 API 健康检查。
7. Sentry-compatible error tracking patterns
7. 兼容 Sentry 的错误追踪模式
Sentry itself is not fully open source in the same way every tool on this list is, but the broader pattern matters: structured errors, release tracking, sourcemaps, and context-rich reporting. Whether using Sentry or another tool, the key habit is to capture errors with enough context to reproduce them. Bad error: “Payment failed”. Useful error: “Payment provider rejected capture, order_id=ord_123, provider=stripe, attempt=2, status=requires_payment_method, release=2026.06.16-4”. That extra context can turn an hour-long investigation into a five-minute fix. Sentry 本身并不像此列表中的其他工具那样完全开源,但更广泛的模式很重要:结构化错误、发布追踪、Source Maps 和富上下文报告。无论使用 Sentry 还是其他工具,关键习惯是捕获带有足够上下文的错误以便重现它们。糟糕的错误信息:“支付失败”。有用的错误信息:“支付提供商拒绝扣款,order_id=ord_123,provider=stripe,attempt=2,status=requires_payment_method,release=2026.06.16-4”。这些额外的上下文可以将一小时的调查缩短为五分钟的修复。
8. Prisma, Drizzle, and query builders: choose based on project needs
8. Prisma、Drizzle 和查询构建器:根据项目需求选择
We do not treat ORMs as religion. Prisma is productive and approachable. Drizzle is lighter and gives excellent TypeScript ergonomics. 我们不把 ORM 当作宗教。Prisma 高效且易于上手。Drizzle 更轻量,并提供了出色的 TypeScript 人体工程学体验。