Integrations should be optional
Production systems often talk to Slack for channel notifications, AWS S3 for media, email providers, SMS, realtime voice, and sometimes AI helpers. Not every environment needs every integration on day one.
Load integration credentials through a dedicated config module. If Slack tokens or AWS keys are absent, disable those code paths cleanly. Core CRUD and auth should still work. That is how local development stays possible without a full cloud account.
Document required versus optional variables in one place. New engineers should know that DATABASE_URL is mandatory while SLACK_BOT_TOKEN is not—before they spend an afternoon debugging a boot failure.
Slack as a mapping, not a secret store in the UI
A common product need is linking a project or workspace to a Slack channel so updates can post there. Store the channel id and display name as domain data. Keep bot and user OAuth tokens only in server environment variables.
When creating or updating a project, upsert the Slack mapping in a side table instead of stuffing tokens into the project row. The UI can show the channel name; it should never show the bot token.
On send, the API reads the mapping, uses the server token, and posts. If Slack is not configured, skip or queue with a visible “integration disabled” state rather than failing the whole business action—unless the action is explicitly “notify Slack.”
Format messages with a small template layer: title, link back to the app, and a short summary. Avoid dumping raw database dumps into Slack; people stop reading noisy channels.
Handle revoked channels gracefully. If Slack returns that a channel is missing, mark the mapping inactive and prompt an admin to reconnect instead of retrying forever.
AWS for files
Object storage (S3-compatible) is the usual home for uploads: logos, attachments, exports. Prefer uploading through the API or via short-lived signed URLs issued by the API after authz checks.
Centralize path helpers so every feature writes under a predictable prefix. Validate content type and size on the server. Clients may encode base64 for small assets, but large files should use direct-to-bucket flows so your API is not a bandwidth bottleneck.
Separate AWS credentials from public site config. Desktop and mobile builds should never embed account keys. They call your API; your API talks to AWS.
Think about deletion and retention. When a user removes an attachment in the product, decide whether the object is deleted immediately, soft-deleted, or lifecycle-expired. Orphaned files quietly raise your bill.
Use least-privilege IAM for the app role: put/get on specific prefixes, not full account admin. Rotate keys on a schedule and keep them out of git history.
Other vendors in the same pattern
SMS, email, voice SDKs, and AI providers fit the same optional-integration mold. Each gets an isConfigured guard, a thin adapter, and domain events that call the adapter—not raw SDK calls sprinkled through controllers.
For realtime voice or video, issue short-lived credentials from the API after authz. Never ship long-lived vendor secrets inside Electron or mobile binaries.
For AI features, keep prompts and keys server-side. Log usage for cost control, and give admins a switch to disable the feature in staging without redeploying clients.
Email providers deserve the same discipline: templates and API keys live on the server; the domain event is “user invited,” not “call SendGrid from three controllers.”
Webhooks and inbound events
Outbound posts are only half of many integrations. Slack interactivity, Stripe events, or storage callbacks arrive as webhooks. Verify signatures, reject unknown sources, and process idempotently—vendors retry.
Keep webhook handlers thin: validate, enqueue or write a durable event, return 200 quickly. Heavy work belongs in a worker so the vendor does not time out and double-deliver.
Store a delivery id or event id when the vendor provides one. Deduplicating on that id prevents double notifications when your worker and their retry overlap.
Expose a safe “replay last event” tool for admins in staging. Debugging integrations without a replay path turns into shared screenshots of vendor dashboards.
Observability without leaking secrets
Log integration outcomes: success, rate limit, auth failure, not-configured skip. Never log full tokens, signed URL query strings, or raw webhook bodies that contain PII you do not need in logs.
Metrics help: count of Slack posts per hour, S3 upload failures, webhook latency. A quiet drop in outbound notifications often means a revoked token, not “users stopped caring.”
Alert on sustained failure, not on a single blip. Vendors have brief outages; your on-call should wake for “Slack failing for 15 minutes,” not one 429.
A shared integration checklist
Whether the vendor is Slack, AWS, Firebase, Twilio, or a voice SDK, the checklist stays similar.
- Secrets in server env only; document required vs optional vars
- isConfigured() guards before calling the vendor
- Domain tables store references (channel id, file key)—not credentials
- Failures are isolated and logged without leaking tokens
- UI explains when an integration is off
- Retries have limits; dead mappings get marked inactive
- Inbound webhooks verify signatures and dedupe event ids
Avoiding integration sprawl
Do not let every feature invent its own Slack client. One notification or messaging adapter keeps retries, rate limits, and formatting consistent.
Prefer explicit feature flags for expensive or sensitive vendors (AI, realtime media). Turning them off in staging should be one env change, not a code fork.
Review third-party scopes regularly. A Slack bot that can only post to a channel is safer than one with broad workspace admin rights you never use.
Write a one-page “integration runbook” for on-call: where secrets live, how to rotate them, and how to disable a vendor quickly if it misbehaves.
Resist “just one more vendor” for the same job. Two SMS providers “for redundancy” often means two half-maintained adapters and unclear routing.
Closing
Optional, server-owned integrations keep products flexible. Slack for collaboration signals, AWS for durable files, and other vendors for specialized jobs should each sit behind a thin adapter with isConfigured guards and verified webhooks.
The goal is not to integrate everything. The goal is to integrate carefully so the core product still works when a vendor is down or a key is missing.