Lessons Learned from Real-World Azure + Bitbucket Deployments
Designing CI/CD pipelines feels straightforwardβ¦
until your builds become inconsistent, deployments start failing, and environment management turns messy.
After working extensively with:
- Bitbucket Pipelines
- Azure App Service (Linux)
- Node.js backends
- React / Angular frontends
- Monorepos
β¦Iβve gathered practical lessons that saved hours of debugging and prevented production issues.
This post focuses on real-world patterns, not textbook theory.
π§± 1οΈβ£ Deterministic Builds Are Non-Negotiable
One of the most common CI/CD mistakes:
npm install
β Why this is risky in pipelines
- Can modify package-lock.json
- Non-reproducible builds
- βWorks locally, fails in CIβ
β
Best Practice
npm ci
For deployment bundles:
npm ci --omit=dev
β Benefits
- Enforces lockfile
- Faster installs
- Reproducible builds
π¦ 2οΈβ£ Donβt Ship Dev Dependencies to Production
Early pipelines often bundle everything:
node_modules/
β Consequences
- Bloated artifacts
- Slower deployments
- Higher memory usage
- Cold start delays
β
Better Strategy
Reinstall only production dependencies:
npm ci --omit=dev
β Result
- Smaller ZIP
- Faster startup
- Cleaner runtime
β 3οΈβ£ Frontend Configuration: The Silent Problem
For React / Angular / Vite apps:
npm run build
π Environment variables become hardcoded into JS bundles
β Symptoms
- Azure App Settings changes do nothing
- Rebuild required per environment
β
Scalable Solution β Runtime Config
Create an env.js file:
window.__env = {
API_URL: "https://api.example.com"
};
Load before the app bundle:
<script src="/env.js"></script>
Access in React:
const apiUrl = window.__env?.API_URL;
β Advantages
- Build once, deploy anywhere
- No rebuild per environment
- Easy config updates
- Customer-specific deployments
π 4οΈβ£ Frontend Secrets? They Donβt Exist.
Important reality:
β You cannot truly hide secrets in frontend apps
Anything delivered to the browser can be inspected.
π¨ Never expose
- API keys
- DB credentials
- Client secrets
β
Correct Architecture
Frontend β Backend β Third-party
Secrets live only in:
- Backend
- Azure App Settings
- Key Vault
π± 5οΈβ£ One YAML vs Multiple YAML Files
As projects grow:
π Should we create separate YAML per branch?
β Why this becomes painful
- Pipeline drift
- Duplication
- Maintenance overhead
β
Recommended
Use one common YAML with branch logic:
branches:
dev:
test:
main:
π 6οΈβ£ Repeated Steps? Use YAML Anchors
Avoid copy-paste pipelines.
β
Use Anchors
definitions:
steps:
- step: &build_step
Reuse:
- step: *build_step
β Benefits
- Cleaner YAML
- Easier updates
- Less duplication
βοΈ 7οΈβ£ Azure Deployment: Pipe vs CLI
Two popular approaches:
β
Atlassian Azure Pipe (Preferred)
pipe: atlassian/azure-web-apps-deploy
β Why itβs great
- Minimal scripting
- Cleaner pipelines
- Fewer failure points
π§° Azure CLI (Use When Needed)
Best for:
- Slot swaps
- Infra provisioning
- Advanced automation
β Avoid installing CLI manually
curl InstallAzureCLIDeb
β
Use Azure CLI image instead
image: mcr.microsoft.com/azure-cli
π¨ 8οΈβ£ Dangerous Commands to Avoid (Unless Justified)
Examples:
az resource update ... allow=true
PORT=8080 hardcoding
sleep 30 hacks
β Only acceptable when
- Legacy constraints
- Debugging scenarios
- Platform requirements
- Always document the reason.
π 9οΈβ£ Repository vs Deployment Variables
Misusing variables causes many CI/CD issues.
β
Repository Variables
Use for:
- Shared config
- Build constants
Examples:
- NODE_VERSION
- LINT_FLAGS
β
Deployment Variables
Use for:
- Environment-specific values
- Secrets
Examples:
- API_URL
- Azure credentials
π Final Takeaways
β
Pipelines must be deterministic
β
Use npm ci, not npm install
β
Install prod dependencies only
β
Artifacts should be environment-agnostic
β
Never store secrets in frontend
β
Prefer runtime config for SPAs
β
Use one YAML with branch logic
β
Use YAML anchors to avoid duplication
β
Prefer deployment pipes over complex scripts
β
Use Azure CLI only when flexibility required
π Conclusion: CI/CD is Easyβ¦ Until It Isnβt
Setting up a pipeline that βworksβ is not difficult.
But designing a pipeline that is:
- Deterministic
- Environment-agnostic
- Secure
- Scalable
Easy to maintain
π requires deliberate engineering decisions.
Small shortcuts like:
β Using npm install
β Embedding secrets in frontend
β Duplicating YAML per branch
β Shipping devDependencies
β¦may work initially but create serious friction as projects scale.
π₯ Final Thought
Your CI/CD pipeline is not just automationβ¦
π It is part of your system architecture.
Treat it with the same care as application code.
Top comments (0)