| name | gcp |
| description | [Applies to: **/*] This guide provides definitive best practices for developing, deploying, and operating applications on Google Cloud Platform (GCP), emphasizing security, performance, and maintainability. |
| source | cursor_mdc |
gcp Best Practices
Adhering to these guidelines ensures your GCP applications are robust, secure, and performant. Treat these as non-negotiable standards.
Code Organization and Structure
1. Adhere to Google's Language Style Guides
Consistency is paramount. Always follow the official Google Style Guides for your chosen language. This improves readability and maintainability across the team.
Guideline: Integrate linting and formatting tools (e.g., Black for Python, Prettier for JS) configured with Google's style.
2. Infrastructure as Code (IaC) is Mandatory
Provision and manage all GCP resources using IaC. This ensures declarative, version-controlled, and auditable infrastructure. Terraform is the default choice.
Guideline: Treat your infrastructure code with the same rigor as application code.
❌ BAD: Manual console configuration, gcloud commands for provisioning.
✅ GOOD: Terraform for all resource definitions.
# main.tf
resource "google_project_service" "compute_api" {
project = var.project_id
service = "compute.googleapis.com"
disable_on_destroy = false
}
resource "google_compute_instance" "default" {
project = var.project_id
zone = "us-central1-a"
name = "my-app-instance"
machine_type = "e2-medium"
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
}
}
network_interface {
network = "default"
}
}
Common Patterns and Anti-patterns
1. Write Idempotent Functions and Services
Your functions and services must produce the same result regardless of how many times they are called with the same input. This is critical for retries and distributed systems.
❌ BAD: Non-idempotent operation.
def process_order(order_id):
db.update_counter(order_id, -1)
✅ GOOD: Idempotent operation using a transaction or state check.
def process_order_idempotent(order_id):
if not db.order_processed(order_id):
db.process_order(order_id)
db.mark_order_processed(order_id)
else:
print(f"Order {order_id} already processed, skipping.")
2. Ensure HTTP Functions Send a Response
HTTP-triggered Cloud Functions and Cloud Run services must send an HTTP response. Failing to do so results in timeouts and unnecessary billing.
import functions_framework
@functions_framework.http
def hello_http(request):
"""HTTP Cloud Function: Always sends a response."""
name = request.args.get("name", "World")
return f"Hello {name}!"
3. Least Privilege for Service Accounts
Service accounts must have the absolute minimum permissions required. Never embed service account keys in code. Use Workload Identity Federation or attach service accounts to resources.
❌ BAD: Hardcoding service account keys or granting roles/owner.
import os
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "/path/to/key.json"
✅ GOOD: Attaching service account to Cloud Run/Functions/GKE or using Workload Identity.
from google.cloud import storage
client = storage.Client()
4. Firestore Document IDs: Avoid Sequential Values
Monotonically increasing or decreasing document IDs (e.g., item1, item2) can lead to "hotspotting" and contention, severely impacting write performance.
❌ BAD: Sequential document IDs.
db.collection('products').document('product_1').set({'name': 'Widget A'})
db.collection('products').document('product_2').set({'name': 'Widget B'})
✅ GOOD: Use automatic IDs or UUIDs.
db.collection('products').add({'name': 'Widget A'})
import uuid
product_id = str(uuid.uuid4())
db.collection('products').document(product_id).set({'name': 'Widget B'})
Performance Considerations
1. Minimize Cold Start Latency for Serverless
For Cloud Functions and Cloud Run, optimize container startup time by minimizing dependencies and performing heavy computations in global scope.
❌ BAD: Heavy computation on every request.
import time
def expensive_init():
time.sleep(5)
return "Initialized"
def handler(request):
data = expensive_init()
return f"Hello, {data}!"
✅ GOOD: Cache expensive operations in global scope.
import time
GLOBAL_DATA = "Initialized"
time.sleep(5)
def handler(request):
return f"Hello, {GLOBAL_DATA}!"
2. Configure Cloud Run for Background Activities
If your Cloud Run service performs background tasks after responding to an HTTP request, you must use instance-based billing. Otherwise, CPU access will be severely limited.
Guideline: For request-based billing, ensure all asynchronous operations complete before sending a response.
3. Optimize Firestore Indexing
Reduce write latency by setting collection-level index exemptions for fields not used in queries (e.g., large strings, sequential values, TTL fields, large arrays/maps).
Guideline: Only index what you query.
Common Pitfalls and Gotchas
1. Neglecting Temporary Files in Serverless
Files written to /tmp in Cloud Functions/Run consume memory and can persist between invocations. Always delete temporary files to prevent out-of-memory errors.
❌ BAD: Not cleaning up temporary files.
import os
with open('/tmp/data.txt', 'w') as f:
f.write('some data')
✅ GOOD: Explicitly delete temporary files.
import os
temp_file_path = '/tmp/data.txt'
with open(temp_file_path, 'w') as f:
f.write('some data')
os.remove(temp_file_path)
2. Relying on Offsets for Firestore Pagination
Using offset in Firestore queries retrieves all skipped documents internally, billing you for reads and increasing latency.
❌ BAD: Using offset.
query = db.collection('items').order_by('timestamp').offset(10).limit(10)
✅ GOOD: Use cursors for efficient pagination.
last_doc_on_previous_page = ...
query = db.collection('items').order_by('timestamp').start_after(last_doc_on_previous_page).limit(10)
Testing Approaches
1. Implement a Comprehensive CI/CD Pipeline
Adopt Google's "change safety" lifecycle: design, development, qualification, and rollout. This mandates automated testing (unit, integration, E2E), canary deployments, and post-deployment monitoring.
Guideline: Every code change must pass automated tests and be deployed through a controlled, staged release process.
2. Test Infrastructure as Code
Validate your Terraform or Config Connector configurations. Use tools like terraform validate and terraform plan in CI, and consider policy enforcement tools like OPA Gatekeeper.
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
run: terraform plan -out=tfplan