@ryuenn3123/agentic-senior-core 1.8.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.
- package/.agent-context/blueprints/api-nextjs.md +184 -0
- package/.agent-context/blueprints/aspnet-api.md +247 -0
- package/.agent-context/blueprints/ci-github-actions.md +226 -0
- package/.agent-context/blueprints/ci-gitlab.md +200 -0
- package/.agent-context/blueprints/fastapi-service.md +210 -0
- package/.agent-context/blueprints/go-service.md +217 -0
- package/.agent-context/blueprints/graphql-grpc-api.md +51 -0
- package/.agent-context/blueprints/infrastructure-as-code.md +62 -0
- package/.agent-context/blueprints/kubernetes-manifests.md +76 -0
- package/.agent-context/blueprints/laravel-api.md +223 -0
- package/.agent-context/blueprints/nestjs-logic.md +247 -0
- package/.agent-context/blueprints/observability.md +227 -0
- package/.agent-context/blueprints/spring-boot-api.md +218 -0
- package/.agent-context/policies/llm-judge-threshold.json +20 -0
- package/.agent-context/profiles/platform.md +13 -0
- package/.agent-context/profiles/regulated.md +13 -0
- package/.agent-context/profiles/startup.md +13 -0
- package/.agent-context/prompts/init-project.md +86 -0
- package/.agent-context/prompts/refactor.md +45 -0
- package/.agent-context/prompts/review-code.md +47 -0
- package/.agent-context/review-checklists/architecture-review.md +70 -0
- package/.agent-context/review-checklists/frontend-usability.md +33 -0
- package/.agent-context/review-checklists/performance-audit.md +65 -0
- package/.agent-context/review-checklists/pr-checklist.md +97 -0
- package/.agent-context/review-checklists/release-operations.md +29 -0
- package/.agent-context/review-checklists/security-audit.md +113 -0
- package/.agent-context/rules/api-docs.md +186 -0
- package/.agent-context/rules/architecture.md +198 -0
- package/.agent-context/rules/database-design.md +202 -0
- package/.agent-context/rules/efficiency-vs-hype.md +143 -0
- package/.agent-context/rules/error-handling.md +234 -0
- package/.agent-context/rules/event-driven.md +226 -0
- package/.agent-context/rules/frontend-architecture.md +66 -0
- package/.agent-context/rules/git-workflow.md +200 -0
- package/.agent-context/rules/microservices.md +174 -0
- package/.agent-context/rules/naming-conv.md +141 -0
- package/.agent-context/rules/performance.md +168 -0
- package/.agent-context/rules/realtime.md +47 -0
- package/.agent-context/rules/security.md +195 -0
- package/.agent-context/rules/testing.md +178 -0
- package/.agent-context/stacks/csharp.md +149 -0
- package/.agent-context/stacks/go.md +181 -0
- package/.agent-context/stacks/java.md +135 -0
- package/.agent-context/stacks/php.md +178 -0
- package/.agent-context/stacks/python.md +153 -0
- package/.agent-context/stacks/ruby.md +80 -0
- package/.agent-context/stacks/rust.md +86 -0
- package/.agent-context/stacks/typescript.md +317 -0
- package/.agent-context/state/architecture-map.md +25 -0
- package/.agent-context/state/dependency-map.md +32 -0
- package/.agent-override.md +36 -0
- package/.agents/workflows/init-project.md +29 -0
- package/.agents/workflows/refactor.md +29 -0
- package/.agents/workflows/review-code.md +29 -0
- package/.cursorrules +140 -0
- package/.gemini/instructions.md +97 -0
- package/.github/ISSUE_TEMPLATE/v1.7-frontend-work-item.yml +54 -0
- package/.github/copilot-instructions.md +104 -0
- package/.github/workflows/benchmark-detection.yml +38 -0
- package/.github/workflows/frontend-usability-gate.yml +36 -0
- package/.github/workflows/release-gate.yml +32 -0
- package/.github/workflows/sbom-compliance.yml +32 -0
- package/.windsurfrules +106 -0
- package/AGENTS.md +131 -0
- package/CONTRIBUTING.md +136 -0
- package/LICENSE +21 -0
- package/README.md +239 -0
- package/bin/agentic-senior-core.js +1147 -0
- package/mcp.json +29 -0
- package/package.json +50 -0
- package/scripts/detection-benchmark.mjs +138 -0
- package/scripts/frontend-usability-audit.mjs +87 -0
- package/scripts/generate-sbom.mjs +61 -0
- package/scripts/init-project.ps1 +105 -0
- package/scripts/init-project.sh +131 -0
- package/scripts/llm-judge.mjs +664 -0
- package/scripts/release-gate.mjs +116 -0
- package/scripts/validate.mjs +554 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Go Stack Profile — Simple, Explicit, Fast
|
|
2
|
+
|
|
3
|
+
> Go's superpower is simplicity. Don't fight it.
|
|
4
|
+
> No magic, no abstractions for the sake of abstractions.
|
|
5
|
+
|
|
6
|
+
## Core Principles
|
|
7
|
+
|
|
8
|
+
1. **Accept interfaces, return structs** — keep APIs flexible, implementations concrete
|
|
9
|
+
2. **Errors are values** — handle them explicitly, don't panic
|
|
10
|
+
3. **Stdlib first** — Go's standard library is excellent, use it before reaching for packages
|
|
11
|
+
4. **Small interfaces** — 1-2 methods max. `io.Reader` has one method for a reason
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Error Handling (The Go Way)
|
|
16
|
+
|
|
17
|
+
### Rule: Always Check Errors, Never Ignore
|
|
18
|
+
|
|
19
|
+
```go
|
|
20
|
+
// BANNED: Ignoring errors
|
|
21
|
+
result, _ := doSomething()
|
|
22
|
+
json.Unmarshal(data, &out) // Error silently ignored
|
|
23
|
+
|
|
24
|
+
// REQUIRED: Handle every error
|
|
25
|
+
result, err := doSomething()
|
|
26
|
+
if err != nil {
|
|
27
|
+
return fmt.Errorf("failed to do something: %w", err)
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Wrapping Errors for Context
|
|
32
|
+
```go
|
|
33
|
+
// BANNED: Bare error return
|
|
34
|
+
if err != nil {
|
|
35
|
+
return err // Caller has no idea where this came from
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// REQUIRED: Wrap with context using %w
|
|
39
|
+
if err != nil {
|
|
40
|
+
return fmt.Errorf("creating user %q: %w", req.Email, err)
|
|
41
|
+
}
|
|
42
|
+
// Produces: "creating user "jane@example.com": duplicate key value"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Custom Error Types
|
|
46
|
+
```go
|
|
47
|
+
type NotFoundError struct {
|
|
48
|
+
Resource string
|
|
49
|
+
ID string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func (e *NotFoundError) Error() string {
|
|
53
|
+
return fmt.Sprintf("%s not found: %s", e.Resource, e.ID)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check with errors.As
|
|
57
|
+
var notFound *NotFoundError
|
|
58
|
+
if errors.As(err, ¬Found) {
|
|
59
|
+
http.Error(w, notFound.Error(), http.StatusNotFound)
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Project Structure
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
project-name/
|
|
69
|
+
├── cmd/
|
|
70
|
+
│ └── server/
|
|
71
|
+
│ └── main.go # Entry point
|
|
72
|
+
│
|
|
73
|
+
├── internal/ # Private application code
|
|
74
|
+
│ ├── user/
|
|
75
|
+
│ │ ├── handler.go # HTTP handlers (transport)
|
|
76
|
+
│ │ ├── service.go # Business logic
|
|
77
|
+
│ │ ├── repository.go # Data access
|
|
78
|
+
│ │ ├── model.go # Domain types
|
|
79
|
+
│ │ └── user_test.go # Tests alongside code
|
|
80
|
+
│ │
|
|
81
|
+
│ ├── order/
|
|
82
|
+
│ │ └── ...
|
|
83
|
+
│ │
|
|
84
|
+
│ └── platform/ # Cross-cutting infrastructure
|
|
85
|
+
│ ├── config/
|
|
86
|
+
│ │ └── config.go # Env-validated configuration
|
|
87
|
+
│ ├── database/
|
|
88
|
+
│ │ └── postgres.go # DB connection setup
|
|
89
|
+
│ ├── logger/
|
|
90
|
+
│ │ └── logger.go # Structured logging (slog)
|
|
91
|
+
│ └── middleware/
|
|
92
|
+
│ ├── auth.go
|
|
93
|
+
│ └── logging.go
|
|
94
|
+
│
|
|
95
|
+
├── api/ # API definitions (OpenAPI, proto)
|
|
96
|
+
│ └── openapi.yaml
|
|
97
|
+
│
|
|
98
|
+
├── go.mod
|
|
99
|
+
├── go.sum
|
|
100
|
+
├── Makefile # Build/test/lint commands
|
|
101
|
+
└── Dockerfile
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Key Rule: `internal/` is Private
|
|
105
|
+
Everything in `internal/` is invisible to external importers. This is Go's built-in encapsulation. Use it.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Interface Design
|
|
110
|
+
|
|
111
|
+
```go
|
|
112
|
+
// BANNED: Large interfaces
|
|
113
|
+
type UserService interface {
|
|
114
|
+
Create(ctx context.Context, req CreateUserReq) (*User, error)
|
|
115
|
+
Update(ctx context.Context, id string, req UpdateUserReq) (*User, error)
|
|
116
|
+
Delete(ctx context.Context, id string) error
|
|
117
|
+
GetByID(ctx context.Context, id string) (*User, error)
|
|
118
|
+
GetByEmail(ctx context.Context, email string) (*User, error)
|
|
119
|
+
ListAll(ctx context.Context) ([]*User, error)
|
|
120
|
+
// 10 more methods...
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// REQUIRED: Small, focused interfaces (1-3 methods)
|
|
124
|
+
type UserCreator interface {
|
|
125
|
+
Create(ctx context.Context, req CreateUserReq) (*User, error)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
type UserFinder interface {
|
|
129
|
+
GetByID(ctx context.Context, id string) (*User, error)
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Rule:** Define interfaces where they are CONSUMED, not where they are implemented.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Context Usage
|
|
138
|
+
|
|
139
|
+
```go
|
|
140
|
+
// REQUIRED: First parameter is always context.Context
|
|
141
|
+
func (s *UserService) Create(ctx context.Context, req CreateUserReq) (*User, error) {
|
|
142
|
+
// Pass context to all downstream calls
|
|
143
|
+
user, err := s.repo.Insert(ctx, req)
|
|
144
|
+
// ...
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// BANNED: context.Background() deep inside application code
|
|
148
|
+
// Use it ONLY at the entry point (main, HTTP handler setup, test setup)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Preferred Libraries
|
|
154
|
+
|
|
155
|
+
| Need | Library | Why |
|
|
156
|
+
|------|---------|-----|
|
|
157
|
+
| HTTP router | `net/http` (Go 1.22+) / `chi` | Stdlib router is now excellent |
|
|
158
|
+
| Logging | `log/slog` (stdlib) | Structured, leveled, built-in since Go 1.21 |
|
|
159
|
+
| Configuration | `caarlos0/env` + struct tags | Simple, typed env parsing |
|
|
160
|
+
| Database | `database/sql` + `pgx` / `sqlc` | `sqlc` generates type-safe Go from SQL |
|
|
161
|
+
| Migration | `golang-migrate/migrate` | SQL-based, driver-agnostic |
|
|
162
|
+
| Testing | stdlib `testing` + `testify` | `testify` for assertions only |
|
|
163
|
+
| Validation | `go-playground/validator` | Struct tag validation |
|
|
164
|
+
| HTTP client | `net/http` (stdlib) | Sufficient for most needs |
|
|
165
|
+
| JSON | `encoding/json` (stdlib) / `json-iterator` | Stdlib first |
|
|
166
|
+
| API docs | `swaggo/swag` | Auto-generates OpenAPI from comments |
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Banned Patterns
|
|
171
|
+
|
|
172
|
+
| Pattern | Why | Alternative |
|
|
173
|
+
|---------|-----|-------------|
|
|
174
|
+
| `_ = err` or ignoring error | Silent failures | Always handle `err` |
|
|
175
|
+
| `panic()` in library code | Crashes the program | Return errors |
|
|
176
|
+
| `init()` functions | Hidden side effects, hard to test | Explicit initialization |
|
|
177
|
+
| Global mutable state | Concurrency bugs, untestable | Dependency injection |
|
|
178
|
+
| `interface{}` / `any` everywhere | No type safety | Generics (Go 1.18+) or specific types |
|
|
179
|
+
| ORM magic (GORM) | Hidden queries, N+1 traps | `sqlc` or raw `database/sql` |
|
|
180
|
+
| Huge packages | Violates SRP | Split into focused packages |
|
|
181
|
+
| Shared `utils` package | Kitchen sink, circular deps | Domain-specific helpers |
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Java Stack Profile — Enterprise Without the Bloat
|
|
2
|
+
|
|
3
|
+
> Java can be clean. It just needs discipline.
|
|
4
|
+
> Stop writing 200-line classes for a 10-line operation.
|
|
5
|
+
|
|
6
|
+
## Language Version: Java 25+ (LTS)
|
|
7
|
+
|
|
8
|
+
Java 25 is the latest LTS release (September 2025, supported until 2033). Use modern Java features aggressively. Do not write pre-Java 21 style code.
|
|
9
|
+
|
|
10
|
+
### Records Over POJOs
|
|
11
|
+
```java
|
|
12
|
+
// BANNED: 80-line POJO with getters, setters, equals, hashCode
|
|
13
|
+
public class UserDto {
|
|
14
|
+
private String name;
|
|
15
|
+
private String email;
|
|
16
|
+
public String getName() { return name; }
|
|
17
|
+
public void setName(String name) { this.name = name; }
|
|
18
|
+
// ... 60 more lines of boilerplate
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// REQUIRED: Record (immutable, auto-generates equals/hashCode/toString)
|
|
22
|
+
public record UserDto(String name, String email) {}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Sealed Classes for Domain Variants
|
|
26
|
+
```java
|
|
27
|
+
// Model fixed set of states or outcomes
|
|
28
|
+
public sealed interface PaymentResult
|
|
29
|
+
permits PaymentResult.Success, PaymentResult.Failed, PaymentResult.Pending {
|
|
30
|
+
|
|
31
|
+
record Success(String transactionId, BigDecimal amount) implements PaymentResult {}
|
|
32
|
+
record Failed(String reason, String errorCode) implements PaymentResult {}
|
|
33
|
+
record Pending(String retryAfter) implements PaymentResult {}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Pattern Matching
|
|
38
|
+
```java
|
|
39
|
+
// Use switch expressions with pattern matching (Java 21)
|
|
40
|
+
return switch (result) {
|
|
41
|
+
case Success s -> ResponseEntity.ok(s);
|
|
42
|
+
case Failed f -> ResponseEntity.badRequest().body(f.reason());
|
|
43
|
+
case Pending p -> ResponseEntity.accepted().body(p);
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Validation at Boundaries: Jakarta Bean Validation
|
|
50
|
+
|
|
51
|
+
```java
|
|
52
|
+
public record CreateUserRequest(
|
|
53
|
+
@NotBlank @Size(max = 100) String name,
|
|
54
|
+
@NotBlank @Email String email,
|
|
55
|
+
@Min(13) @Max(150) int age
|
|
56
|
+
) {}
|
|
57
|
+
|
|
58
|
+
@PostMapping("/users")
|
|
59
|
+
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest request) {
|
|
60
|
+
// request is validated by the framework before reaching this method
|
|
61
|
+
return ResponseEntity.status(201).body(userService.create(request));
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Project Structure (Spring Boot 4)
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
project-name/
|
|
71
|
+
├── src/main/java/com/example/project/
|
|
72
|
+
│ ├── Application.java # Entry point
|
|
73
|
+
│ │
|
|
74
|
+
│ ├── modules/
|
|
75
|
+
│ │ ├── user/
|
|
76
|
+
│ │ │ ├── UserController.java # Transport (REST)
|
|
77
|
+
│ │ │ ├── UserService.java # Business logic
|
|
78
|
+
│ │ │ ├── UserRepository.java # Data access (Spring Data)
|
|
79
|
+
│ │ │ ├── dto/
|
|
80
|
+
│ │ │ │ ├── CreateUserRequest.java # Input validation (record)
|
|
81
|
+
│ │ │ │ └── UserResponse.java # Output (record)
|
|
82
|
+
│ │ │ ├── entity/
|
|
83
|
+
│ │ │ │ └── UserEntity.java # JPA entity
|
|
84
|
+
│ │ │ └── exception/
|
|
85
|
+
│ │ │ └── UserNotFoundException.java
|
|
86
|
+
│ │ └── order/
|
|
87
|
+
│ │ └── ...
|
|
88
|
+
│ │
|
|
89
|
+
│ └── shared/
|
|
90
|
+
│ ├── config/ # Configuration classes
|
|
91
|
+
│ ├── exception/
|
|
92
|
+
│ │ └── GlobalExceptionHandler.java # @ControllerAdvice
|
|
93
|
+
│ ├── security/ # Security config
|
|
94
|
+
│ └── util/ # Cross-cutting utilities
|
|
95
|
+
│
|
|
96
|
+
├── src/main/resources/
|
|
97
|
+
│ ├── application.yml
|
|
98
|
+
│ └── db/migration/ # Flyway migrations
|
|
99
|
+
│
|
|
100
|
+
└── src/test/java/com/example/project/
|
|
101
|
+
└── modules/user/
|
|
102
|
+
└── UserServiceTest.java
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Preferred Libraries
|
|
108
|
+
|
|
109
|
+
| Need | Library | Why |
|
|
110
|
+
|------|---------|-----|
|
|
111
|
+
| Framework | Spring Boot 4.x (Spring Framework 7) | Industry standard, virtual threads native support |
|
|
112
|
+
| Validation | Jakarta Bean Validation | Built into Spring Boot |
|
|
113
|
+
| ORM | Spring Data JPA / Hibernate | Standard, powerful |
|
|
114
|
+
| Migration | Flyway | SQL-based, version controlled |
|
|
115
|
+
| Mapping | MapStruct | Compile-time, type-safe DTO mapping |
|
|
116
|
+
| Testing | JUnit 5 + Mockito + Testcontainers | Standard stack |
|
|
117
|
+
| HTTP client | Spring RestClient / WebClient | RestClient (sync, new in Boot 4), WebClient (reactive) |
|
|
118
|
+
| Logging | SLF4J + Logback (or Log4j2) | Standard, structured JSON output |
|
|
119
|
+
| Build | Gradle (Kotlin DSL) or Maven | Gradle preferred for modern projects |
|
|
120
|
+
| API docs | springdoc-openapi | Auto-generates OpenAPI from code |
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Banned Patterns
|
|
125
|
+
|
|
126
|
+
| Pattern | Why | Alternative |
|
|
127
|
+
|---------|-----|-------------|
|
|
128
|
+
| Raw `String` for IDs | No type safety | `UUID` or typed ID wrapper |
|
|
129
|
+
| `null` returns | NullPointerException bait | `Optional<T>` for query results |
|
|
130
|
+
| Checked Exception abuse | Forces catch-or-throw chains | Unchecked `RuntimeException` subclasses |
|
|
131
|
+
| Field injection (`@Autowired`) | Hidden deps, untestable | Constructor injection |
|
|
132
|
+
| `System.out.println` | Not structured, not configurable | SLF4J logger |
|
|
133
|
+
| Deep class hierarchies | Fragile, hard to reason | Composition + interfaces |
|
|
134
|
+
| God services (500+ lines) | Violates SRP | Split into focused services |
|
|
135
|
+
| `@Transactional` on controller | Layer leak | Only on service methods |
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# PHP Stack Profile — Modern PHP, Not Legacy PHP
|
|
2
|
+
|
|
3
|
+
> PHP 8.x is a different language from PHP 5.
|
|
4
|
+
> If your AI writes PHP without type declarations, reject it immediately.
|
|
5
|
+
|
|
6
|
+
## Language Version: PHP 8.5+ (Latest Stable)
|
|
7
|
+
|
|
8
|
+
PHP 8.5 is stable since November 2025. Use modern PHP features including the pipe operator (`|>`), `Clone With`, and readonly classes.
|
|
9
|
+
|
|
10
|
+
### Strict Types Everywhere
|
|
11
|
+
```php
|
|
12
|
+
<?php
|
|
13
|
+
// REQUIRED: First line of EVERY PHP file
|
|
14
|
+
declare(strict_types=1);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Typed Properties, Parameters, and Returns
|
|
18
|
+
```php
|
|
19
|
+
// BANNED: Untyped PHP
|
|
20
|
+
function getUser($id) {
|
|
21
|
+
$user = $this->db->find($id);
|
|
22
|
+
return $user;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// REQUIRED: Full type declarations
|
|
26
|
+
function getUser(int $id): ?User {
|
|
27
|
+
return $this->userRepository->find($id);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Enums (PHP 8.1+)
|
|
32
|
+
```php
|
|
33
|
+
// BANNED: Magic strings
|
|
34
|
+
$status = 'pending';
|
|
35
|
+
|
|
36
|
+
// REQUIRED: Backed enums
|
|
37
|
+
enum OrderStatus: string {
|
|
38
|
+
case Pending = 'pending';
|
|
39
|
+
case Confirmed = 'confirmed';
|
|
40
|
+
case Shipped = 'shipped';
|
|
41
|
+
case Delivered = 'delivered';
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Readonly Properties and Classes (PHP 8.2+) and Pipe Operator (PHP 8.5+)
|
|
46
|
+
```php
|
|
47
|
+
// Readonly for DTOs and value objects
|
|
48
|
+
readonly class CreateUserDto {
|
|
49
|
+
public function __construct(
|
|
50
|
+
public string $name,
|
|
51
|
+
public string $email,
|
|
52
|
+
public int $age,
|
|
53
|
+
) {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Pipe operator for cleaner function chains (PHP 8.5)
|
|
57
|
+
$result = $input
|
|
58
|
+
|> 'trim'
|
|
59
|
+
|> 'strtolower'
|
|
60
|
+
|> fn($s) => str_replace(' ', '-', $s);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Validation at Boundaries: Laravel Form Requests
|
|
66
|
+
|
|
67
|
+
```php
|
|
68
|
+
// BANNED: Validating in controller body
|
|
69
|
+
public function store(Request $request) {
|
|
70
|
+
$data = $request->all(); // Raw, unvalidated!
|
|
71
|
+
User::create($data); // Mass assignment vulnerability!
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// REQUIRED: Form Request class
|
|
75
|
+
class StoreUserRequest extends FormRequest {
|
|
76
|
+
public function rules(): array {
|
|
77
|
+
return [
|
|
78
|
+
'name' => ['required', 'string', 'max:100'],
|
|
79
|
+
'email' => ['required', 'email', 'unique:users'],
|
|
80
|
+
'age' => ['required', 'integer', 'min:13', 'max:150'],
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public function store(StoreUserRequest $request): JsonResponse {
|
|
86
|
+
$user = $this->userService->create($request->validated());
|
|
87
|
+
return response()->json($user, 201);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Project Structure (Laravel)
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
project-name/
|
|
97
|
+
├── app/
|
|
98
|
+
│ ├── Modules/ # Feature-based grouping
|
|
99
|
+
│ │ ├── User/
|
|
100
|
+
│ │ │ ├── Controllers/
|
|
101
|
+
│ │ │ │ └── UserController.php # Transport
|
|
102
|
+
│ │ │ ├── Services/
|
|
103
|
+
│ │ │ │ └── UserService.php # Business logic
|
|
104
|
+
│ │ │ ├── Repositories/
|
|
105
|
+
│ │ │ │ └── UserRepository.php # Data access
|
|
106
|
+
│ │ │ ├── Requests/
|
|
107
|
+
│ │ │ │ └── StoreUserRequest.php
|
|
108
|
+
│ │ │ ├── Resources/
|
|
109
|
+
│ │ │ │ └── UserResource.php # API response transformer
|
|
110
|
+
│ │ │ ├── Models/
|
|
111
|
+
│ │ │ │ └── User.php # Eloquent model
|
|
112
|
+
│ │ │ └── Policies/
|
|
113
|
+
│ │ │ └── UserPolicy.php # Authorization
|
|
114
|
+
│ │ └── Order/
|
|
115
|
+
│ │ └── ...
|
|
116
|
+
│ │
|
|
117
|
+
│ ├── Shared/
|
|
118
|
+
│ │ ├── Exceptions/
|
|
119
|
+
│ │ │ └── Handler.php
|
|
120
|
+
│ │ └── Middleware/
|
|
121
|
+
│
|
|
122
|
+
├── database/migrations/
|
|
123
|
+
├── routes/api.php
|
|
124
|
+
├── tests/
|
|
125
|
+
│ ├── Feature/
|
|
126
|
+
│ └── Unit/
|
|
127
|
+
├── phpstan.neon # Static analysis config
|
|
128
|
+
└── composer.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Standards
|
|
134
|
+
|
|
135
|
+
### PSR Compliance
|
|
136
|
+
- **PSR-4:** Autoloading (Composer handles this)
|
|
137
|
+
- **PSR-12:** Coding style (use PHP-CS-Fixer or Pint)
|
|
138
|
+
|
|
139
|
+
### Static Analysis: PHPStan Level 8+
|
|
140
|
+
```neon
|
|
141
|
+
# phpstan.neon
|
|
142
|
+
parameters:
|
|
143
|
+
level: 8
|
|
144
|
+
paths:
|
|
145
|
+
- app
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Preferred Libraries
|
|
151
|
+
|
|
152
|
+
| Need | Library | Why |
|
|
153
|
+
|------|---------|-----|
|
|
154
|
+
| Framework | Laravel 12 | Most productive PHP framework, auto eager loading, GraphQL |
|
|
155
|
+
| Validation | Laravel Form Requests | Built-in, declarative |
|
|
156
|
+
| ORM | Eloquent | Convention over configuration |
|
|
157
|
+
| Testing | PHPUnit / Pest | Pest preferred for readability |
|
|
158
|
+
| Static analysis | PHPStan (level 8+) | Catch type errors at build time |
|
|
159
|
+
| Formatting | Laravel Pint | Zero-config PSR-12 formatter |
|
|
160
|
+
| API resources | Laravel API Resources | Clean response transformation |
|
|
161
|
+
| Auth | Laravel Sanctum / Passport | Token-based auth |
|
|
162
|
+
| Queue | Laravel Queues | Built-in, multiple drivers |
|
|
163
|
+
| API docs | Scribe or L5-Swagger | Auto-generated OpenAPI |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Banned Patterns
|
|
168
|
+
|
|
169
|
+
| Pattern | Why | Alternative |
|
|
170
|
+
|---------|-----|-------------|
|
|
171
|
+
| Missing `declare(strict_types=1)` | Loose type coercion | Always declare |
|
|
172
|
+
| `$request->all()` in `create()` | Mass assignment vulnerability | `$request->validated()` |
|
|
173
|
+
| Raw SQL with concatenation | SQL injection | Eloquent or query builder with bindings |
|
|
174
|
+
| `dd()` / `dump()` in production | Debug leak | Structured logging |
|
|
175
|
+
| God controllers (500+ lines) | Violates SRP | Thin controllers, fat services |
|
|
176
|
+
| Business logic in models | Model becomes unmaintainable | Service layer |
|
|
177
|
+
| `try { } catch (\Exception $e) { }` | Swallows everything | Specific exception types |
|
|
178
|
+
| Dynamic properties (deprecated 8.2) | Runtime errors | Declared typed properties |
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Python Stack Profile — Explicit is Better Than Implicit
|
|
2
|
+
|
|
3
|
+
> Python's readability is a gift. Don't waste it with sloppy typing and god functions.
|
|
4
|
+
|
|
5
|
+
## Type System (Enforced)
|
|
6
|
+
|
|
7
|
+
### Type Hints Everywhere (Python 3.12+)
|
|
8
|
+
```python
|
|
9
|
+
# BANNED: Untyped function signatures
|
|
10
|
+
def process_data(data, options):
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
# REQUIRED: Full type annotations
|
|
14
|
+
def process_order(order: Order, options: ProcessingOptions) -> OrderResult:
|
|
15
|
+
...
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Rule:** Every function MUST have type annotations for all parameters and return types. Use `mypy --strict` or `pyright` in strict mode.
|
|
19
|
+
|
|
20
|
+
### No `Any` (Same Rule as TypeScript)
|
|
21
|
+
```python
|
|
22
|
+
# BANNED
|
|
23
|
+
def handle(data: Any) -> Any: ...
|
|
24
|
+
result: dict[str, Any] = get_response()
|
|
25
|
+
|
|
26
|
+
# REQUIRED
|
|
27
|
+
def handle(data: OrderPayload) -> OrderResult: ...
|
|
28
|
+
result: OrderResponse = get_response()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Validation at Boundaries: Pydantic
|
|
34
|
+
|
|
35
|
+
### Rule: ALL External Data MUST Pass Through Pydantic
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
# BANNED: Trusting raw dicts
|
|
39
|
+
@app.post("/users")
|
|
40
|
+
async def create_user(request: Request):
|
|
41
|
+
data = await request.json() # Could be anything!
|
|
42
|
+
return await user_service.create(data)
|
|
43
|
+
|
|
44
|
+
# REQUIRED: Pydantic model at the boundary
|
|
45
|
+
from pydantic import BaseModel, EmailStr, Field
|
|
46
|
+
|
|
47
|
+
class CreateUserRequest(BaseModel):
|
|
48
|
+
name: str = Field(min_length=1, max_length=100)
|
|
49
|
+
email: EmailStr
|
|
50
|
+
age: int = Field(ge=13, le=150)
|
|
51
|
+
|
|
52
|
+
@app.post("/users")
|
|
53
|
+
async def create_user(payload: CreateUserRequest) -> UserResponse:
|
|
54
|
+
return await user_service.create(payload)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Pydantic Best Practices
|
|
58
|
+
- Use `Field()` with constraints (`min_length`, `ge`, `le`, `pattern`)
|
|
59
|
+
- Use `model_config = ConfigDict(strict=True)` to prevent type coercion
|
|
60
|
+
- Derive response models from base models: `class UserResponse(UserBase):`
|
|
61
|
+
- Use `model_validator` for cross-field validation
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Project Structure
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
project-name/
|
|
69
|
+
├── src/
|
|
70
|
+
│ ├── __init__.py
|
|
71
|
+
│ ├── main.py # Application entry point
|
|
72
|
+
│ ├── config.py # Pydantic Settings (env validation)
|
|
73
|
+
│ │
|
|
74
|
+
│ ├── modules/ # Feature modules
|
|
75
|
+
│ │ ├── user/
|
|
76
|
+
│ │ │ ├── __init__.py
|
|
77
|
+
│ │ │ ├── router.py # Transport (API routes)
|
|
78
|
+
│ │ │ ├── service.py # Business logic
|
|
79
|
+
│ │ │ ├── repository.py # Data access
|
|
80
|
+
│ │ │ ├── schemas.py # Pydantic models (DTOs)
|
|
81
|
+
│ │ │ ├── models.py # SQLAlchemy/ORM models
|
|
82
|
+
│ │ │ └── exceptions.py # Domain-specific errors
|
|
83
|
+
│ │ └── order/
|
|
84
|
+
│ │ └── ...
|
|
85
|
+
│ │
|
|
86
|
+
│ └── shared/
|
|
87
|
+
│ ├── errors.py # Base error classes
|
|
88
|
+
│ ├── middleware.py # Auth, logging, error handling
|
|
89
|
+
│ ├── database.py # DB session management
|
|
90
|
+
│ └── logger.py # Structured logging (structlog)
|
|
91
|
+
│
|
|
92
|
+
├── tests/
|
|
93
|
+
│ ├── conftest.py # Fixtures
|
|
94
|
+
│ ├── factories.py # Test data factories
|
|
95
|
+
│ └── modules/
|
|
96
|
+
│ └── user/
|
|
97
|
+
│ └── test_user_service.py
|
|
98
|
+
│
|
|
99
|
+
├── pyproject.toml # Project config (single source)
|
|
100
|
+
├── .env.example
|
|
101
|
+
└── Dockerfile
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Async Patterns
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# BANNED: Sync I/O in async context
|
|
110
|
+
import requests # Blocks the event loop!
|
|
111
|
+
response = requests.get("https://api.example.com")
|
|
112
|
+
|
|
113
|
+
# REQUIRED: Use async HTTP client
|
|
114
|
+
import httpx
|
|
115
|
+
async with httpx.AsyncClient() as client:
|
|
116
|
+
response = await client.get("https://api.example.com")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Rule:** In async applications (FastAPI, etc.), NEVER use synchronous I/O libraries (`requests`, `time.sleep`, `open()` for large files). Use `httpx`, `asyncio.sleep`, `aiofiles`.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Preferred Libraries (2025)
|
|
124
|
+
|
|
125
|
+
| Need | Library | Why |
|
|
126
|
+
|------|---------|-----|
|
|
127
|
+
| Web framework | `fastapi` | Async, type-safe, auto OpenAPI docs |
|
|
128
|
+
| Validation | `pydantic` v2 | Fast, Rust-powered, zero compromise |
|
|
129
|
+
| ORM | `sqlalchemy` 2.0+ / `sqlmodel` | Mature, async support. SQLModel wraps SQLAlchemy + Pydantic |
|
|
130
|
+
| HTTP client | `httpx` | Async-native, requests-compatible API |
|
|
131
|
+
| Testing | `pytest` + `pytest-asyncio` | Standard, plugin-rich |
|
|
132
|
+
| Linting | `ruff` | 10-100x faster than flake8+isort+black combined |
|
|
133
|
+
| Formatting | `ruff format` (or `black`) | Consistent, zero-config |
|
|
134
|
+
| Type checking | `mypy --strict` or `pyright` | Catch type errors before runtime |
|
|
135
|
+
| Logging | `structlog` | Structured, JSON-ready, contextvars |
|
|
136
|
+
| Env config | `pydantic-settings` | Type-safe env with validation |
|
|
137
|
+
| Password | `passlib[bcrypt]` or `argon2-cffi` | Proven, secure |
|
|
138
|
+
| Migration | `alembic` | SQLAlchemy migration standard |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Banned Patterns
|
|
143
|
+
|
|
144
|
+
| Pattern | Why | Alternative |
|
|
145
|
+
|---------|-----|-------------|
|
|
146
|
+
| `Any` type | Defeats type checking | Specific types or `Unknown` protocol |
|
|
147
|
+
| `requests` in async | Blocks event loop | `httpx` |
|
|
148
|
+
| `print()` for logging | No structure, no levels | `structlog` or `logging` |
|
|
149
|
+
| `except Exception: pass` | Swallows every error | Specific exceptions, always log |
|
|
150
|
+
| `from module import *` | Namespace pollution | Explicit imports |
|
|
151
|
+
| Mutable default args | Shared state bug | `def f(items: list | None = None):` |
|
|
152
|
+
| Global state | Untestable, concurrency bugs | Dependency injection |
|
|
153
|
+
| `os.environ["KEY"]` | Crashes with KeyError | `pydantic-settings` with defaults |
|