@malamute/ai-rules 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +270 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/.claude/rules/conventions/documentation.md +324 -0
  4. package/configs/_shared/.claude/rules/conventions/git.md +265 -0
  5. package/configs/_shared/.claude/rules/{performance.md → conventions/performance.md} +1 -1
  6. package/configs/_shared/.claude/rules/conventions/principles.md +334 -0
  7. package/configs/_shared/.claude/rules/devops/ci-cd.md +262 -0
  8. package/configs/_shared/.claude/rules/devops/docker.md +275 -0
  9. package/configs/_shared/.claude/rules/devops/nx.md +194 -0
  10. package/configs/_shared/.claude/rules/domain/backend/api-design.md +203 -0
  11. package/configs/_shared/.claude/rules/lang/csharp/async.md +220 -0
  12. package/configs/_shared/.claude/rules/lang/csharp/csharp.md +314 -0
  13. package/configs/_shared/.claude/rules/lang/csharp/linq.md +210 -0
  14. package/configs/_shared/.claude/rules/lang/python/async.md +337 -0
  15. package/configs/_shared/.claude/rules/lang/python/celery.md +476 -0
  16. package/configs/_shared/.claude/rules/lang/python/config.md +339 -0
  17. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/database/sqlalchemy.md +6 -1
  18. package/configs/_shared/.claude/rules/lang/python/deployment.md +523 -0
  19. package/configs/_shared/.claude/rules/lang/python/error-handling.md +330 -0
  20. package/configs/_shared/.claude/rules/lang/python/migrations.md +421 -0
  21. package/configs/_shared/.claude/rules/lang/python/python.md +172 -0
  22. package/configs/_shared/.claude/rules/lang/python/repository.md +383 -0
  23. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/testing.md +2 -69
  24. package/configs/_shared/.claude/rules/lang/typescript/async.md +447 -0
  25. package/configs/_shared/.claude/rules/lang/typescript/generics.md +356 -0
  26. package/configs/_shared/.claude/rules/lang/typescript/typescript.md +212 -0
  27. package/configs/_shared/.claude/rules/quality/error-handling.md +48 -0
  28. package/configs/_shared/.claude/rules/quality/logging.md +45 -0
  29. package/configs/_shared/.claude/rules/quality/observability.md +240 -0
  30. package/configs/_shared/.claude/rules/quality/testing-patterns.md +65 -0
  31. package/configs/_shared/.claude/rules/security/secrets-management.md +222 -0
  32. package/configs/_shared/.claude/skills/analysis/explore/SKILL.md +257 -0
  33. package/configs/_shared/.claude/skills/analysis/security-audit/SKILL.md +184 -0
  34. package/configs/_shared/.claude/skills/dev/api-endpoint/SKILL.md +126 -0
  35. package/configs/_shared/.claude/{commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  36. package/configs/_shared/.claude/{commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  37. package/configs/_shared/.claude/{commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  38. package/configs/_shared/.claude/skills/infra/deploy/SKILL.md +139 -0
  39. package/configs/_shared/.claude/skills/infra/docker/SKILL.md +95 -0
  40. package/configs/_shared/.claude/skills/infra/migration/SKILL.md +158 -0
  41. package/configs/_shared/.claude/skills/nx/nx-affected/SKILL.md +72 -0
  42. package/configs/_shared/.claude/skills/nx/nx-lib/SKILL.md +375 -0
  43. package/configs/_shared/CLAUDE.md +52 -149
  44. package/configs/angular/.claude/rules/{components.md → core/components.md} +69 -15
  45. package/configs/angular/.claude/rules/core/resource.md +285 -0
  46. package/configs/angular/.claude/rules/core/signals.md +323 -0
  47. package/configs/angular/.claude/rules/http.md +338 -0
  48. package/configs/angular/.claude/rules/routing.md +291 -0
  49. package/configs/angular/.claude/rules/ssr.md +312 -0
  50. package/configs/angular/.claude/rules/state/signal-store.md +408 -0
  51. package/configs/angular/.claude/rules/{state.md → state/state.md} +2 -2
  52. package/configs/angular/.claude/rules/testing.md +7 -7
  53. package/configs/angular/.claude/rules/ui/aria.md +422 -0
  54. package/configs/angular/.claude/rules/ui/forms.md +424 -0
  55. package/configs/angular/.claude/rules/ui/pipes-directives.md +335 -0
  56. package/configs/angular/.claude/settings.json +1 -0
  57. package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +362 -0
  58. package/configs/angular/.claude/skills/signal-store/SKILL.md +445 -0
  59. package/configs/angular/CLAUDE.md +24 -216
  60. package/configs/dotnet/.claude/rules/background-services.md +552 -0
  61. package/configs/dotnet/.claude/rules/configuration.md +426 -0
  62. package/configs/dotnet/.claude/rules/ddd.md +447 -0
  63. package/configs/dotnet/.claude/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/.claude/rules/mediatr.md +320 -0
  65. package/configs/dotnet/.claude/rules/middleware.md +489 -0
  66. package/configs/dotnet/.claude/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/.claude/rules/validation.md +388 -0
  68. package/configs/dotnet/.claude/settings.json +21 -3
  69. package/configs/dotnet/CLAUDE.md +53 -286
  70. package/configs/fastapi/.claude/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/.claude/rules/dependencies.md +170 -0
  72. package/configs/{python → fastapi}/.claude/rules/fastapi.md +61 -1
  73. package/configs/fastapi/.claude/rules/lifespan.md +274 -0
  74. package/configs/fastapi/.claude/rules/middleware.md +229 -0
  75. package/configs/fastapi/.claude/rules/pydantic.md +433 -0
  76. package/configs/fastapi/.claude/rules/responses.md +251 -0
  77. package/configs/fastapi/.claude/rules/routers.md +202 -0
  78. package/configs/fastapi/.claude/rules/security.md +222 -0
  79. package/configs/fastapi/.claude/rules/testing.md +251 -0
  80. package/configs/fastapi/.claude/rules/websockets.md +298 -0
  81. package/configs/fastapi/.claude/settings.json +33 -0
  82. package/configs/fastapi/CLAUDE.md +144 -0
  83. package/configs/flask/.claude/rules/blueprints.md +208 -0
  84. package/configs/flask/.claude/rules/cli.md +285 -0
  85. package/configs/flask/.claude/rules/configuration.md +281 -0
  86. package/configs/flask/.claude/rules/context.md +238 -0
  87. package/configs/flask/.claude/rules/error-handlers.md +278 -0
  88. package/configs/flask/.claude/rules/extensions.md +278 -0
  89. package/configs/flask/.claude/rules/flask.md +171 -0
  90. package/configs/flask/.claude/rules/marshmallow.md +206 -0
  91. package/configs/flask/.claude/rules/security.md +267 -0
  92. package/configs/flask/.claude/rules/testing.md +284 -0
  93. package/configs/flask/.claude/settings.json +33 -0
  94. package/configs/flask/CLAUDE.md +166 -0
  95. package/configs/nestjs/.claude/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/.claude/rules/filters.md +376 -0
  97. package/configs/nestjs/.claude/rules/interceptors.md +317 -0
  98. package/configs/nestjs/.claude/rules/middleware.md +321 -0
  99. package/configs/nestjs/.claude/rules/modules.md +26 -0
  100. package/configs/nestjs/.claude/rules/pipes.md +351 -0
  101. package/configs/nestjs/.claude/rules/websockets.md +451 -0
  102. package/configs/nestjs/.claude/settings.json +16 -2
  103. package/configs/nestjs/CLAUDE.md +57 -215
  104. package/configs/nextjs/.claude/rules/api-routes.md +358 -0
  105. package/configs/nextjs/.claude/rules/authentication.md +355 -0
  106. package/configs/nextjs/.claude/rules/components.md +52 -0
  107. package/configs/nextjs/.claude/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/.claude/rules/database.md +400 -0
  109. package/configs/nextjs/.claude/rules/middleware.md +303 -0
  110. package/configs/nextjs/.claude/rules/routing.md +324 -0
  111. package/configs/nextjs/.claude/rules/seo.md +350 -0
  112. package/configs/nextjs/.claude/rules/server-actions.md +353 -0
  113. package/configs/nextjs/.claude/rules/state/zustand.md +6 -6
  114. package/configs/nextjs/.claude/settings.json +5 -0
  115. package/configs/nextjs/CLAUDE.md +69 -331
  116. package/package.json +23 -9
  117. package/src/cli.js +220 -0
  118. package/src/config.js +29 -0
  119. package/src/index.js +13 -0
  120. package/src/installer.js +361 -0
  121. package/src/merge.js +116 -0
  122. package/src/tech-config.json +29 -0
  123. package/src/utils.js +96 -0
  124. package/configs/python/.claude/rules/flask.md +0 -332
  125. package/configs/python/.claude/settings.json +0 -18
  126. package/configs/python/CLAUDE.md +0 -273
  127. package/src/install.js +0 -315
  128. /package/configs/_shared/.claude/rules/{accessibility.md → domain/frontend/accessibility.md} +0 -0
  129. /package/configs/_shared/.claude/rules/{security.md → security/security.md} +0 -0
  130. /package/configs/_shared/.claude/skills/{debug → dev/debug}/SKILL.md +0 -0
  131. /package/configs/_shared/.claude/skills/{learning → dev/learning}/SKILL.md +0 -0
  132. /package/configs/_shared/.claude/skills/{spec → dev/spec}/SKILL.md +0 -0
  133. /package/configs/_shared/.claude/skills/{review → git/review}/SKILL.md +0 -0
@@ -0,0 +1,476 @@
1
+ ---
2
+ paths:
3
+ - "**/tasks/**/*.py"
4
+ - "**/celery.py"
5
+ - "**/celeryconfig.py"
6
+ - "**/*_task.py"
7
+ - "**/*_tasks.py"
8
+ ---
9
+
10
+ # Python Celery
11
+
12
+ ## Configuration
13
+
14
+ ```python
15
+ # celery.py
16
+ from celery import Celery
17
+ from kombu import Queue, Exchange
18
+
19
+ app = Celery("myapp")
20
+
21
+ app.config_from_object({
22
+ # Broker
23
+ "broker_url": "redis://localhost:6379/0",
24
+ "result_backend": "redis://localhost:6379/1",
25
+
26
+ # Serialization
27
+ "task_serializer": "json",
28
+ "result_serializer": "json",
29
+ "accept_content": ["json"],
30
+
31
+ # Task settings
32
+ "task_acks_late": True,
33
+ "task_reject_on_worker_lost": True,
34
+ "task_time_limit": 300, # 5 minutes hard limit
35
+ "task_soft_time_limit": 240, # 4 minutes soft limit
36
+
37
+ # Result settings
38
+ "result_expires": 3600, # 1 hour
39
+ "result_extended": True,
40
+
41
+ # Worker settings
42
+ "worker_prefetch_multiplier": 1,
43
+ "worker_concurrency": 4,
44
+
45
+ # Task routing
46
+ "task_queues": [
47
+ Queue("default", Exchange("default"), routing_key="default"),
48
+ Queue("high_priority", Exchange("high_priority"), routing_key="high_priority"),
49
+ Queue("low_priority", Exchange("low_priority"), routing_key="low_priority"),
50
+ ],
51
+ "task_default_queue": "default",
52
+ "task_routes": {
53
+ "app.tasks.email.*": {"queue": "high_priority"},
54
+ "app.tasks.reports.*": {"queue": "low_priority"},
55
+ },
56
+
57
+ # Beat scheduler
58
+ "beat_scheduler": "celery.beat:PersistentScheduler",
59
+ "beat_schedule_filename": "celerybeat-schedule",
60
+ })
61
+
62
+ # Auto-discover tasks
63
+ app.autodiscover_tasks(["app.tasks"])
64
+ ```
65
+
66
+ ## Basic Tasks
67
+
68
+ ```python
69
+ # tasks/email.py
70
+ from celery import shared_task
71
+ from celery.exceptions import MaxRetriesExceededError
72
+ import logging
73
+
74
+ logger = logging.getLogger(__name__)
75
+
76
+
77
+ @shared_task(
78
+ bind=True,
79
+ max_retries=3,
80
+ default_retry_delay=60,
81
+ autoretry_for=(Exception,),
82
+ retry_backoff=True,
83
+ retry_backoff_max=600,
84
+ retry_jitter=True,
85
+ )
86
+ def send_email(self, to: str, subject: str, body: str) -> dict:
87
+ """Send email with automatic retry."""
88
+ try:
89
+ logger.info(f"Sending email to {to}")
90
+ # Email sending logic
91
+ return {"status": "sent", "to": to}
92
+
93
+ except Exception as exc:
94
+ logger.error(f"Failed to send email: {exc}")
95
+ raise self.retry(exc=exc)
96
+
97
+
98
+ @shared_task(bind=True)
99
+ def send_bulk_emails(self, recipients: list[str], subject: str, body: str) -> dict:
100
+ """Send emails in bulk with progress tracking."""
101
+ total = len(recipients)
102
+ successful = 0
103
+ failed = []
104
+
105
+ for i, recipient in enumerate(recipients):
106
+ try:
107
+ send_email.delay(recipient, subject, body)
108
+ successful += 1
109
+ except Exception as e:
110
+ failed.append({"email": recipient, "error": str(e)})
111
+
112
+ # Update progress
113
+ self.update_state(
114
+ state="PROGRESS",
115
+ meta={"current": i + 1, "total": total, "successful": successful},
116
+ )
117
+
118
+ return {"total": total, "successful": successful, "failed": failed}
119
+ ```
120
+
121
+ ## Task with Database
122
+
123
+ ```python
124
+ # tasks/orders.py
125
+ from celery import shared_task
126
+ from sqlalchemy.orm import Session
127
+ from app.db.session import SessionLocal
128
+ from app.models import Order, OrderStatus
129
+ from contextlib import contextmanager
130
+
131
+
132
+ @contextmanager
133
+ def get_db():
134
+ db = SessionLocal()
135
+ try:
136
+ yield db
137
+ finally:
138
+ db.close()
139
+
140
+
141
+ @shared_task(bind=True)
142
+ def process_order(self, order_id: str) -> dict:
143
+ """Process an order."""
144
+ with get_db() as db:
145
+ order = db.query(Order).filter(Order.id == order_id).first()
146
+
147
+ if not order:
148
+ return {"status": "error", "message": "Order not found"}
149
+
150
+ try:
151
+ order.status = OrderStatus.PROCESSING
152
+ db.commit()
153
+
154
+ # Process order logic
155
+ process_payment(order)
156
+ reserve_inventory(order)
157
+ schedule_shipping(order)
158
+
159
+ order.status = OrderStatus.COMPLETED
160
+ db.commit()
161
+
162
+ return {"status": "completed", "order_id": order_id}
163
+
164
+ except Exception as e:
165
+ order.status = OrderStatus.FAILED
166
+ order.error_message = str(e)
167
+ db.commit()
168
+ raise
169
+ ```
170
+
171
+ ## Chaining and Groups
172
+
173
+ ```python
174
+ # tasks/workflows.py
175
+ from celery import shared_task, chain, group, chord
176
+
177
+
178
+ @shared_task
179
+ def fetch_data(url: str) -> dict:
180
+ # Fetch data from URL
181
+ return {"data": "..."}
182
+
183
+
184
+ @shared_task
185
+ def process_data(data: dict) -> dict:
186
+ # Process the data
187
+ return {"processed": True}
188
+
189
+
190
+ @shared_task
191
+ def save_data(data: dict) -> dict:
192
+ # Save to database
193
+ return {"saved": True}
194
+
195
+
196
+ @shared_task
197
+ def aggregate_results(results: list) -> dict:
198
+ # Aggregate all results
199
+ return {"aggregated": results}
200
+
201
+
202
+ # Chain: Sequential execution
203
+ def run_pipeline(url: str):
204
+ workflow = chain(
205
+ fetch_data.s(url),
206
+ process_data.s(),
207
+ save_data.s(),
208
+ )
209
+ return workflow.apply_async()
210
+
211
+
212
+ # Group: Parallel execution
213
+ def process_multiple_urls(urls: list[str]):
214
+ job = group(fetch_data.s(url) for url in urls)
215
+ return job.apply_async()
216
+
217
+
218
+ # Chord: Parallel with callback
219
+ def process_and_aggregate(urls: list[str]):
220
+ workflow = chord(
221
+ group(fetch_data.s(url) for url in urls),
222
+ aggregate_results.s(),
223
+ )
224
+ return workflow.apply_async()
225
+ ```
226
+
227
+ ## Scheduled Tasks (Beat)
228
+
229
+ ```python
230
+ # celery.py
231
+ from celery.schedules import crontab
232
+
233
+ app.conf.beat_schedule = {
234
+ # Run every minute
235
+ "check-pending-orders": {
236
+ "task": "app.tasks.orders.check_pending_orders",
237
+ "schedule": 60.0,
238
+ },
239
+
240
+ # Run every hour
241
+ "cleanup-expired-sessions": {
242
+ "task": "app.tasks.cleanup.cleanup_sessions",
243
+ "schedule": crontab(minute=0),
244
+ },
245
+
246
+ # Run daily at midnight
247
+ "generate-daily-report": {
248
+ "task": "app.tasks.reports.generate_daily_report",
249
+ "schedule": crontab(hour=0, minute=0),
250
+ },
251
+
252
+ # Run every Monday at 9am
253
+ "send-weekly-digest": {
254
+ "task": "app.tasks.email.send_weekly_digest",
255
+ "schedule": crontab(hour=9, minute=0, day_of_week=1),
256
+ },
257
+
258
+ # Run on first day of month
259
+ "generate-monthly-invoice": {
260
+ "task": "app.tasks.billing.generate_monthly_invoices",
261
+ "schedule": crontab(hour=0, minute=0, day_of_month=1),
262
+ },
263
+ }
264
+ ```
265
+
266
+ ## Task Monitoring
267
+
268
+ ```python
269
+ # tasks/base.py
270
+ from celery import Task
271
+ from celery.signals import (
272
+ task_prerun,
273
+ task_postrun,
274
+ task_failure,
275
+ task_success,
276
+ )
277
+ import time
278
+ import logging
279
+
280
+ logger = logging.getLogger(__name__)
281
+
282
+
283
+ class BaseTask(Task):
284
+ """Base task with monitoring."""
285
+
286
+ abstract = True
287
+
288
+ def on_failure(self, exc, task_id, args, kwargs, einfo):
289
+ logger.error(
290
+ f"Task {self.name}[{task_id}] failed: {exc}",
291
+ extra={"task_id": task_id, "args": args, "kwargs": kwargs},
292
+ )
293
+
294
+ def on_success(self, retval, task_id, args, kwargs):
295
+ logger.info(
296
+ f"Task {self.name}[{task_id}] succeeded",
297
+ extra={"task_id": task_id, "result": retval},
298
+ )
299
+
300
+ def on_retry(self, exc, task_id, args, kwargs, einfo):
301
+ logger.warning(
302
+ f"Task {self.name}[{task_id}] retrying: {exc}",
303
+ extra={"task_id": task_id},
304
+ )
305
+
306
+
307
+ @task_prerun.connect
308
+ def task_prerun_handler(task_id, task, args, kwargs, **extras):
309
+ task.start_time = time.time()
310
+
311
+
312
+ @task_postrun.connect
313
+ def task_postrun_handler(task_id, task, args, kwargs, retval, state, **extras):
314
+ duration = time.time() - getattr(task, "start_time", time.time())
315
+ logger.info(f"Task {task.name} completed in {duration:.2f}s")
316
+
317
+
318
+ # Usage
319
+ @shared_task(base=BaseTask)
320
+ def my_task():
321
+ pass
322
+ ```
323
+
324
+ ## Task Cancellation
325
+
326
+ ```python
327
+ # tasks/long_running.py
328
+ from celery import shared_task
329
+ from celery.exceptions import SoftTimeLimitExceeded
330
+
331
+
332
+ @shared_task(bind=True, soft_time_limit=300)
333
+ def long_running_task(self, data: dict) -> dict:
334
+ """Task that respects cancellation."""
335
+ results = []
336
+
337
+ for i, item in enumerate(data["items"]):
338
+ # Check if task was revoked
339
+ if self.is_aborted():
340
+ return {"status": "aborted", "processed": i}
341
+
342
+ try:
343
+ result = process_item(item)
344
+ results.append(result)
345
+
346
+ except SoftTimeLimitExceeded:
347
+ # Graceful shutdown
348
+ return {"status": "timeout", "processed": i, "results": results}
349
+
350
+ return {"status": "completed", "results": results}
351
+
352
+
353
+ # Revoking tasks
354
+ from celery.result import AsyncResult
355
+
356
+ def cancel_task(task_id: str):
357
+ result = AsyncResult(task_id)
358
+ result.revoke(terminate=True)
359
+ ```
360
+
361
+ ## FastAPI Integration
362
+
363
+ ```python
364
+ # api/tasks.py
365
+ from fastapi import APIRouter, HTTPException
366
+ from celery.result import AsyncResult
367
+ from app.celery import app as celery_app
368
+ from app.tasks.orders import process_order
369
+
370
+ router = APIRouter(prefix="/tasks", tags=["tasks"])
371
+
372
+
373
+ @router.post("/orders/{order_id}/process")
374
+ async def start_order_processing(order_id: str) -> dict:
375
+ task = process_order.delay(order_id)
376
+ return {"task_id": task.id, "status": "started"}
377
+
378
+
379
+ @router.get("/{task_id}")
380
+ async def get_task_status(task_id: str) -> dict:
381
+ result = AsyncResult(task_id, app=celery_app)
382
+
383
+ if result.ready():
384
+ if result.successful():
385
+ return {"status": "completed", "result": result.get()}
386
+ else:
387
+ return {"status": "failed", "error": str(result.result)}
388
+
389
+ return {
390
+ "status": result.state,
391
+ "progress": result.info if result.info else None,
392
+ }
393
+
394
+
395
+ @router.delete("/{task_id}")
396
+ async def cancel_task(task_id: str) -> dict:
397
+ result = AsyncResult(task_id, app=celery_app)
398
+ result.revoke(terminate=True)
399
+ return {"status": "cancelled", "task_id": task_id}
400
+ ```
401
+
402
+ ## Commands
403
+
404
+ ```bash
405
+ # Start worker
406
+ celery -A app.celery worker --loglevel=info
407
+
408
+ # Start worker with queues
409
+ celery -A app.celery worker -Q high_priority,default --loglevel=info
410
+
411
+ # Start beat scheduler
412
+ celery -A app.celery beat --loglevel=info
413
+
414
+ # Start flower (monitoring)
415
+ celery -A app.celery flower --port=5555
416
+
417
+ # Inspect active tasks
418
+ celery -A app.celery inspect active
419
+
420
+ # Purge queue
421
+ celery -A app.celery purge
422
+ ```
423
+
424
+ ## Anti-patterns
425
+
426
+ ```python
427
+ # BAD: Database connection in task without cleanup
428
+ @shared_task
429
+ def bad_task():
430
+ db = SessionLocal() # Never closed!
431
+ db.query(...)
432
+
433
+
434
+ # GOOD: Use context manager
435
+ @shared_task
436
+ def good_task():
437
+ with get_db() as db:
438
+ db.query(...)
439
+
440
+
441
+ # BAD: Passing ORM objects
442
+ @shared_task
443
+ def bad_task(user: User): # Can't serialize!
444
+ pass
445
+
446
+
447
+ # GOOD: Pass IDs, fetch in task
448
+ @shared_task
449
+ def good_task(user_id: str):
450
+ with get_db() as db:
451
+ user = db.query(User).get(user_id)
452
+
453
+
454
+ # BAD: No retry limit
455
+ @shared_task(autoretry_for=(Exception,)) # Infinite retries!
456
+ def bad_task():
457
+ pass
458
+
459
+
460
+ # GOOD: Set retry limits
461
+ @shared_task(autoretry_for=(Exception,), max_retries=3)
462
+ def good_task():
463
+ pass
464
+
465
+
466
+ # BAD: Blocking synchronous calls
467
+ @shared_task
468
+ def bad_task():
469
+ time.sleep(300) # Blocks worker
470
+
471
+
472
+ # GOOD: Use appropriate timeouts
473
+ @shared_task(soft_time_limit=60, time_limit=120)
474
+ def good_task():
475
+ pass
476
+ ```