From 704830098a18bb83ed54292b14e5841f12c3e6c6 Mon Sep 17 00:00:00 2001 From: Raishin Date: Tue, 24 Mar 2026 22:14:18 +0400 Subject: [PATCH] feat: add code-simplifier power --- README.md | 7 + code-simplifier/POWER.md | 175 +++++ code-simplifier/mcp.json | 19 + code-simplifier/steering/c.md | 751 ++++++++++++++++++ code-simplifier/steering/core.md | 549 +++++++++++++ code-simplifier/steering/csharp.md | 777 ++++++++++++++++++ code-simplifier/steering/java-spring.md | 435 ++++++++++ code-simplifier/steering/java.md | 571 ++++++++++++++ code-simplifier/steering/js.md | 594 ++++++++++++++ code-simplifier/steering/kotlin.md | 443 +++++++++++ code-simplifier/steering/php-laravel.md | 638 +++++++++++++++ code-simplifier/steering/php-symfony.md | 753 ++++++++++++++++++ code-simplifier/steering/php.md | 543 +++++++++++++ code-simplifier/steering/python-django.md | 466 +++++++++++ code-simplifier/steering/python-fastapi.md | 364 +++++++++ code-simplifier/steering/python-fastmcp.md | 442 +++++++++++ code-simplifier/steering/python-flask.md | 422 ++++++++++ code-simplifier/steering/python.md | 540 +++++++++++++ code-simplifier/steering/rust.md | 601 ++++++++++++++ code-simplifier/steering/typescript.md | 873 +++++++++++++++++++++ 20 files changed, 9963 insertions(+) create mode 100644 code-simplifier/POWER.md create mode 100644 code-simplifier/mcp.json create mode 100644 code-simplifier/steering/c.md create mode 100644 code-simplifier/steering/core.md create mode 100644 code-simplifier/steering/csharp.md create mode 100644 code-simplifier/steering/java-spring.md create mode 100644 code-simplifier/steering/java.md create mode 100644 code-simplifier/steering/js.md create mode 100644 code-simplifier/steering/kotlin.md create mode 100644 code-simplifier/steering/php-laravel.md create mode 100644 code-simplifier/steering/php-symfony.md create mode 100644 code-simplifier/steering/php.md create mode 100644 code-simplifier/steering/python-django.md create mode 100644 code-simplifier/steering/python-fastapi.md create mode 100644 code-simplifier/steering/python-fastmcp.md create mode 100644 code-simplifier/steering/python-flask.md create mode 100644 code-simplifier/steering/python.md create mode 100644 code-simplifier/steering/rust.md create mode 100644 code-simplifier/steering/typescript.md diff --git a/README.md b/README.md index a45289d..3105af8 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,13 @@ Documentation is available at https://kiro.dev/docs/powers/ --- +### code-simplifier +**Code Simplifier** - Simplify and refine code across languages and frameworks for clarity, consistency, and maintainability while preserving functionality. + +**MCP Servers:** context7 + +--- + ### datadog **Datadog Observability** - Query logs, metrics, traces, RUM events, incidents, and monitors from Datadog for production debugging and performance analysis. diff --git a/code-simplifier/POWER.md b/code-simplifier/POWER.md new file mode 100644 index 0000000..9d237c5 --- /dev/null +++ b/code-simplifier/POWER.md @@ -0,0 +1,175 @@ +--- +name: "code-simplifier" +displayName: "Code Simplifier" +description: "Simplify and refine code across languages and frameworks for clarity, consistency, and maintainability while preserving behavior." +keywords: ["refactoring", "simplification", "clean-code", "maintainability", "readability", "code-review"] +author: "Raishin" +--- + +# Code Simplifier + +## Overview + +Code Simplifier helps you make behavior-preserving refactors that improve readability, consistency, and maintainability. It combines general simplification rules with language-specific and framework-specific steering so you can clean up code without drifting away from the repository's conventions. + +Use this power when you want to reduce nesting, remove duplication, improve naming, simplify control flow, or align code with established framework patterns while keeping behavior the same. + +## Onboarding + +### Local Installation + +1. Open the Kiro Powers UI. +2. Add a custom power from a local directory. +3. Select the `code-simplifier` power directory. +4. Confirm the power appears in the installed list. + +### MCP Setup + +This is a Guided MCP Power because it depends on Context7 lookups in Kiro. + +1. Ensure `CONTEXT7_API_KEY` is available in the environment used by Kiro. +2. Verify the power includes `mcp.json` with a `context7` server definition. +3. Install or update the power in Kiro so the MCP configuration is reloaded. + +### Verification + +After installation, verify: + +- the power appears in Kiro's installed powers list +- the `context7` MCP server connects successfully +- the power activates for refactoring or code simplification requests +- the steering files are available when the agent needs stack-specific guidance + +## Available Steering Files + +This power includes focused steering files for general rules, languages, and frameworks: + +- **core** - Universal simplification rules and safety boundaries +- **c** - C simplification patterns and memory-aware cleanup +- **csharp** - C# readability, async flow, and .NET conventions +- **java** - Modern Java simplification and maintainable OO patterns +- **java-spring** - Spring Boot layering, DI, and controller/service cleanup +- **js** - JavaScript readability and control-flow simplification +- **kotlin** - Kotlin idioms, null-safety, and coroutine-aware cleanup +- **php** - General PHP readability and maintainability guidance +- **php-laravel** - Laravel conventions for controllers, services, and Eloquent code +- **php-symfony** - Symfony service and controller simplification patterns +- **python** - Pythonic cleanup and readability improvements +- **python-django** - Django views, models, and QuerySet simplification +- **python-fastapi** - FastAPI route, schema, and dependency cleanup +- **python-fastmcp** - FastMCP server and tool simplification guidance +- **python-flask** - Flask blueprint and application structure cleanup +- **rust** - Rust ownership-aware simplification and error handling +- **typescript** - TypeScript and TSX simplification patterns + +All conceptual guidance is in this `POWER.md`. The steering files provide stack-specific guidance you can load as needed. + +To load a specific guide: + +```text +Call action "readSteering" with powerName="code-simplifier", steeringFile="core.md" +``` + +## When to Use This Power + +- Simplifying recently modified code without changing behavior +- Refactoring controllers, services, components, functions, or small modules +- Reducing nesting and clarifying conditionals +- Removing duplication when the shared abstraction stays readable +- Aligning code with current language or framework conventions +- Reviewing a change for maintainability and readability issues + +## Available MCP Servers + +### context7 + +**Purpose:** Query current library and framework documentation when best-practice guidance depends on version or ecosystem conventions. + +**Available tools:** +- `resolve-library-id` - Find the correct documentation target +- `query-docs` - Fetch current documentation and examples for a library or framework + +## Tool Usage Examples + +### Resolve a Documentation Target + +```text +Use tool "resolve-library-id" with: +- libraryName: "laravel" +- query: "I want current Laravel controller and Eloquent simplification guidance" +``` + +### Query Current Best Practices + +```text +Use tool "query-docs" with: +- libraryId: "/laravel/docs" +- query: "controller validation best practices" +``` + +## Recommended Workflow + +1. Read `core.md` first to establish the safety boundaries for behavior-preserving cleanup. +2. Load the language steering file that matches the code you are touching. +3. Load the framework steering file only when the file clearly depends on that framework. +4. Use Context7 when current best practices or version-specific behavior matters. +5. Keep the change local unless broader cleanup is explicitly requested. + +## Example Workflow + +### Simplify a Laravel Controller + +1. Activate the power for a request such as: "simplify this Laravel controller without changing behavior". +2. Read `core.md` to apply the behavior-preserving rules first. +3. Read `php.md` for general PHP cleanup guidance. +4. Read `php-laravel.md` because controller and Eloquent patterns depend on Laravel conventions. +5. Call `resolve-library-id` with: + - `libraryName`: `laravel` + - `query`: `controller validation and relationship loading best practices` +6. Call `query-docs` with: + - `libraryId`: the resolved Laravel docs id + - `query`: `controller validation best practices` +7. Apply the smallest refactor that improves clarity without changing interfaces, side effects, or request lifecycle behavior. +8. Re-check that the result is more readable, still local in scope, and still follows repository conventions. + +## Best Practices + +- Preserve behavior, public interfaces, and side effects unless change is explicitly requested. +- Prefer clarity over brevity. +- Use guard clauses, extraction, and clearer control flow to reduce nesting. +- Keep abstractions only when they improve readability. +- Avoid dense or clever rewrites that make the code harder to reason about. +- Follow the repository's existing style unless there is a clear reason not to. + +## Configuration + +This power expects Context7 to be configured through `code-simplifier/mcp.json`. + +- Set `CONTEXT7_API_KEY` in the environment used by Kiro before enabling the power. +- If Context7 is unavailable, you can still use the steering files, but version-sensitive best-practice checks will be weaker. +- Keep `autoApprove` limited to the read-only Context7 tools already defined in `mcp.json`. + +## Troubleshooting + +### The refactor changes behavior + +- Narrow the scope and start from the smallest safe cleanup. +- Re-check interfaces, side effects, and lifecycle ordering. +- Use the stack-specific steering file before broadening the change. + +### The right simplification is unclear + +- Load `core.md`, then the relevant language guide. +- Add the framework guide only if it materially affects the code shape. +- Use Context7 to verify current conventions when versions or APIs matter. + +### The code is already dense for good reason + +- Organize the code rather than forcing it to be shorter. +- Prefer explicit helpers and comments over aggressive collapsing. + +### Context7 lookups fail + +- Verify that `CONTEXT7_API_KEY` is set in the environment used by Kiro. +- Confirm the `context7` server in `code-simplifier/mcp.json` still points to `@upstash/context7-mcp`. +- Fall back to the relevant steering file if the external docs lookup is temporarily unavailable. diff --git a/code-simplifier/mcp.json b/code-simplifier/mcp.json new file mode 100644 index 0000000..f9c7a49 --- /dev/null +++ b/code-simplifier/mcp.json @@ -0,0 +1,19 @@ +{ + "mcpServers": { + "context7": { + "command": "npx", + "args": [ + "-y", + "@upstash/context7-mcp", + "--api-key", + "${CONTEXT7_API_KEY}" + ], + "env": {}, + "disabled": false, + "autoApprove": [ + "resolve-library-id", + "query-docs" + ] + } + } +} \ No newline at end of file diff --git a/code-simplifier/steering/c.md b/code-simplifier/steering/c.md new file mode 100644 index 0000000..bb50221 --- /dev/null +++ b/code-simplifier/steering/c.md @@ -0,0 +1,751 @@ +# C Code Simplification Guide + +This guide covers C-specific simplification patterns and +best practices for writing clean, maintainable C code. + +## Table of Contents + +1. [C Style and Conventions](#c-style-and-conventions) +2. [Memory Management](#memory-management) +3. [Pointers and Arrays](#pointers-and-arrays) +4. [Error Handling](#error-handling) +5. [Structs and Data Structures](#structs-and-data-structures) +6. [Preprocessor](#preprocessor) +7. [Function Design](#function-design) +8. [Common Patterns](#common-patterns) + +--- + +## C Style and Conventions + +### Naming Conventions + +```c +// Variables and functions - snake_case +int user_count = 0; +void calculate_total(int items[], int size) { + // implementation +} + +// Constants and macros - UPPER_SNAKE_CASE +#define MAX_BUFFER_SIZE 1024 +#define PI 3.14159 + +// Struct types - snake_case or PascalCase (be consistent) +typedef struct user { + int id; + char name[50]; +} user_t; + +// Enum - UPPER_SNAKE_CASE for values +typedef enum { + STATUS_PENDING, + STATUS_PROCESSING, + STATUS_COMPLETED +} status_t; +``` + +### File Organization + +```c +// header.h +#ifndef HEADER_H +#define HEADER_H + +// Include guards prevent multiple inclusion +#include +#include + +// Function declarations +void init_system(void); +int process_data(const char *input); + +#endif // HEADER_H + +// implementation.c +#include "header.h" + +// Static functions (file-local) +static int helper_function(int x) { + return x * 2; +} + +// Public functions +void init_system(void) { + // implementation +} +``` + +--- + +## Memory Management + +### Always Free Allocated Memory + +```c +// Bad - memory leak +void process_data(void) { + char *buffer = malloc(1024); + if (buffer == NULL) { + return; + } + // ... use buffer + // Missing free()! +} + +// Good - proper cleanup +void process_data(void) { + char *buffer = malloc(1024); + if (buffer == NULL) { + return; + } + + // ... use buffer + + free(buffer); + buffer = NULL; // Prevent use-after-free +} +``` + +### Check malloc Return Values + +```c +// Bad - no null check +int *numbers = malloc(100 * sizeof(int)); +numbers[0] = 42; // Crash if malloc failed! + +// Good - always check +int *numbers = malloc(100 * sizeof(int)); +if (numbers == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + return -1; +} + +numbers[0] = 42; +// ... use numbers + +free(numbers); +``` + +### Use sizeof with Variables, Not Types + +```c +// Bad - error-prone if type changes +int *array = malloc(100 * sizeof(int)); + +// Good - automatically correct if type changes +int *array = malloc(100 * sizeof(*array)); + +// For structs +user_t *user = malloc(sizeof(*user)); +``` + +### Avoid Memory Leaks in Error Paths + +```c +// Bad - leaks on error +int process_file(const char *filename) { + char *buffer = malloc(1024); + FILE *file = fopen(filename, "r"); + + if (file == NULL) { + return -1; // Leaked buffer! + } + + // ... process + + free(buffer); + fclose(file); + return 0; +} + +// Good - cleanup on all paths +int process_file(const char *filename) { + char *buffer = malloc(1024); + if (buffer == NULL) { + return -1; + } + + FILE *file = fopen(filename, "r"); + if (file == NULL) { + free(buffer); + return -1; + } + + // ... process + + fclose(file); + free(buffer); + return 0; +} + +// Better - use goto for cleanup (common C pattern) +int process_file(const char *filename) { + char *buffer = NULL; + FILE *file = NULL; + int result = -1; + + buffer = malloc(1024); + if (buffer == NULL) { + goto cleanup; + } + + file = fopen(filename, "r"); + if (file == NULL) { + goto cleanup; + } + + // ... process + result = 0; + +cleanup: + if (file != NULL) { + fclose(file); + } + free(buffer); // free(NULL) is safe + return result; +} +``` + +--- + +## Pointers and Arrays + +### Const Correctness + +```c +// Bad - no const +void print_string(char *str) { + printf("%s\n", str); +} + +// Good - const for read-only parameters +void print_string(const char *str) { + printf("%s\n", str); +} + +// Const pointer vs pointer to const +const char *ptr1; // Pointer to const char (can't modify data) +char *const ptr2; // Const pointer (can't change pointer) +const char *const ptr3; // Both const +``` + +### Array Parameters + +```c +// Bad - unclear size +void process_array(int arr[]) { + // How big is arr? +} + +// Good - pass size explicitly +void process_array(int arr[], size_t size) { + for (size_t i = 0; i < size; i++) { + arr[i] *= 2; + } +} + +// Better - use const for read-only +void print_array(const int arr[], size_t size) { + for (size_t i = 0; i < size; i++) { + printf("%d ", arr[i]); + } + printf("\n"); +} +``` + +### Pointer Arithmetic + +```c +// Bad - manual indexing with pointers +int sum = 0; +int *p = array; +for (int i = 0; i < size; i++) { + sum += *(p + i); +} + +// Good - use array indexing when clearer +int sum = 0; +for (int i = 0; i < size; i++) { + sum += array[i]; +} + +// Pointer arithmetic when appropriate +void copy_string(char *dest, const char *src) { + while ((*dest++ = *src++) != '\0') { + // Copy until null terminator + } +} +``` + +--- + +## Error Handling + +### Return Error Codes + +```c +// Bad - no error indication +void parse_number(const char *str, int *result) { + *result = atoi(str); // No way to detect errors +} + +// Good - return error code +int parse_number(const char *str, int *result) { + char *endptr; + long val = strtol(str, &endptr, 10); + + if (endptr == str || *endptr != '\0') { + return -1; // Parse error + } + + if (val > INT_MAX || val < INT_MIN) { + return -1; // Overflow + } + + *result = (int)val; + return 0; // Success +} + +// Usage +int number; +if (parse_number("123", &number) != 0) { + fprintf(stderr, "Failed to parse number\n"); + return -1; +} +``` + +### Use errno for System Calls + +```c +#include +#include + +// Check errno after system calls +FILE *file = fopen("data.txt", "r"); +if (file == NULL) { + fprintf(stderr, "Failed to open file: %s\n", strerror(errno)); + return -1; +} +``` + +### Error Handling Macros + +```c +// Define error handling macros +#define CHECK_NULL(ptr, msg) \ + do { \ + if ((ptr) == NULL) { \ + fprintf(stderr, "Error: %s\n", (msg)); \ + goto cleanup; \ + } \ + } while (0) + +#define CHECK_ERROR(expr, msg) \ + do { \ + if ((expr) != 0) { \ + fprintf(stderr, "Error: %s\n", (msg)); \ + goto cleanup; \ + } \ + } while (0) + +// Usage +int process_data(void) { + char *buffer = NULL; + int result = -1; + + buffer = malloc(1024); + CHECK_NULL(buffer, "Memory allocation failed"); + + CHECK_ERROR(read_data(buffer, 1024), "Failed to read data"); + + result = 0; + +cleanup: + free(buffer); + return result; +} +``` + +--- + +## Structs and Data Structures + +### Struct Initialization + +```c +typedef struct { + int id; + char name[50]; + double balance; +} account_t; + +// Bad - uninitialized fields +account_t acc; +acc.id = 1; +// name and balance are uninitialized! + +// Good - designated initializers (C99) +account_t acc = { + .id = 1, + .name = "John Doe", + .balance = 1000.0 +}; + +// Zero initialization +account_t acc = {0}; // All fields set to 0 +``` + +### Opaque Pointers (Information Hiding) + +```c +// header.h +typedef struct user user_t; // Forward declaration + +user_t *user_create(const char *name); +void user_destroy(user_t *user); +const char *user_get_name(const user_t *user); + +// implementation.c +struct user { + char name[50]; + int id; + // Internal fields hidden from users +}; + +user_t *user_create(const char *name) { + user_t *user = malloc(sizeof(*user)); + if (user == NULL) { + return NULL; + } + + strncpy(user->name, name, sizeof(user->name) - 1); + user->name[sizeof(user->name) - 1] = '\0'; + user->id = generate_id(); + + return user; +} + +void user_destroy(user_t *user) { + free(user); +} +``` + +### Flexible Array Members + +```c +// Bad - fixed size +typedef struct { + size_t count; + int data[100]; // Wastes space or limits size +} buffer_t; + +// Good - flexible array member (C99) +typedef struct { + size_t count; + int data[]; // Flexible array +} buffer_t; + +// Allocation +buffer_t *create_buffer(size_t count) { + buffer_t *buf = malloc(sizeof(*buf) + count * sizeof(int)); + if (buf == NULL) { + return NULL; + } + + buf->count = count; + return buf; +} +``` + +--- + +## Preprocessor + +### Include Guards + +```c +// header.h +#ifndef HEADER_H +#define HEADER_H + +// Header content + +#endif // HEADER_H + +// Modern alternative (not standard but widely supported) +#pragma once +``` + +### Macro Best Practices + +```c +// Bad - unsafe macro +#define SQUARE(x) x * x +int result = SQUARE(2 + 3); // Expands to 2 + 3 * 2 + 3 = 11, not 25! + +// Good - parentheses around parameters and expression +#define SQUARE(x) ((x) * (x)) +int result = SQUARE(2 + 3); // Correctly expands to 25 + +// Multi-statement macros - use do-while(0) +#define SWAP(a, b, type) \ + do { \ + type temp = (a); \ + (a) = (b); \ + (b) = temp; \ + } while (0) + +// Usage +if (x > y) + SWAP(x, y, int); // Works correctly with do-while +``` + +### Conditional Compilation + +```c +// Debug logging +#ifdef DEBUG + #define LOG(msg) printf("DEBUG: %s\n", (msg)) +#else + #define LOG(msg) ((void)0) +#endif + +// Platform-specific code +#ifdef _WIN32 + #include + #define PATH_SEPARATOR '\\' +#else + #include + #define PATH_SEPARATOR '/' +#endif +``` + +--- + +## Function Design + +### Single Responsibility + +```c +// Bad - function does too much +void process_user(const char *name, const char *email) { + // Validate input + if (name == NULL || email == NULL) return; + + // Create user + user_t *user = malloc(sizeof(*user)); + + // Save to database + save_to_db(user); + + // Send email + send_welcome_email(email); + + // Log activity + log_user_creation(name); +} + +// Good - separate concerns +int validate_user_input(const char *name, const char *email) { + return (name != NULL && email != NULL) ? 0 : -1; +} + +user_t *create_user(const char *name, const char *email) { + user_t *user = malloc(sizeof(*user)); + if (user == NULL) { + return NULL; + } + + strncpy(user->name, name, sizeof(user->name) - 1); + strncpy(user->email, email, sizeof(user->email) - 1); + + return user; +} + +int process_user(const char *name, const char *email) { + if (validate_user_input(name, email) != 0) { + return -1; + } + + user_t *user = create_user(name, email); + if (user == NULL) { + return -1; + } + + if (save_to_db(user) != 0) { + free(user); + return -1; + } + + send_welcome_email(email); + log_user_creation(name); + + free(user); + return 0; +} +``` + +### Limit Function Parameters + +```c +// Bad - too many parameters +void create_window(int x, int y, int width, int height, + const char *title, int style, int flags, + void *parent, void *menu); + +// Good - use struct for related parameters +typedef struct { + int x, y; + int width, height; + const char *title; + int style; + int flags; + void *parent; + void *menu; +} window_config_t; + +void create_window(const window_config_t *config); + +// Usage with designated initializers +window_config_t config = { + .x = 100, + .y = 100, + .width = 800, + .height = 600, + .title = "My Window", + .style = WINDOW_STYLE_DEFAULT, + .flags = 0, + .parent = NULL, + .menu = NULL +}; + +create_window(&config); +``` + +--- + +## Common Patterns + +### String Handling + +```c +// Always use safe string functions +#include + +// Bad - buffer overflow risk +char dest[10]; +strcpy(dest, source); // Dangerous! + +// Good - bounded copy +char dest[10]; +strncpy(dest, source, sizeof(dest) - 1); +dest[sizeof(dest) - 1] = '\0'; // Ensure null termination + +// Better - use snprintf for formatting +char buffer[100]; +snprintf(buffer, sizeof(buffer), "User: %s, ID: %d", name, id); +``` + +### Dynamic Arrays + +```c +typedef struct { + int *data; + size_t size; + size_t capacity; +} dynamic_array_t; + +dynamic_array_t *array_create(size_t initial_capacity) { + dynamic_array_t *arr = malloc(sizeof(*arr)); + if (arr == NULL) { + return NULL; + } + + arr->data = malloc(initial_capacity * sizeof(int)); + if (arr->data == NULL) { + free(arr); + return NULL; + } + + arr->size = 0; + arr->capacity = initial_capacity; + return arr; +} + +int array_push(dynamic_array_t *arr, int value) { + if (arr->size >= arr->capacity) { + size_t new_capacity = arr->capacity * 2; + int *new_data = realloc(arr->data, new_capacity * sizeof(int)); + if (new_data == NULL) { + return -1; + } + + arr->data = new_data; + arr->capacity = new_capacity; + } + + arr->data[arr->size++] = value; + return 0; +} + +void array_destroy(dynamic_array_t *arr) { + if (arr != NULL) { + free(arr->data); + free(arr); + } +} +``` + +### Callback Functions + +```c +// Function pointer typedef +typedef int (*compare_fn)(const void *a, const void *b); + +// Generic sort function +void sort_array(void *array, size_t count, size_t size, compare_fn compare) { + // Sorting implementation using compare callback +} + +// Comparison function +int compare_ints(const void *a, const void *b) { + int arg1 = *(const int *)a; + int arg2 = *(const int *)b; + return (arg1 > arg2) - (arg1 < arg2); +} + +// Usage +int numbers[] = {5, 2, 8, 1, 9}; +sort_array(numbers, 5, sizeof(int), compare_ints); +``` + +--- + +## C Simplification Checklist + +- [ ] Following C naming conventions +- [ ] Always check malloc/calloc return values +- [ ] Free all allocated memory +- [ ] Use const for read-only parameters +- [ ] Pass array sizes explicitly +- [ ] Return error codes from functions +- [ ] Use designated initializers for structs +- [ ] Include guards in all headers +- [ ] Safe string handling (strncpy, snprintf) +- [ ] Parentheses in macro definitions +- [ ] goto for cleanup in error paths (when appropriate) +- [ ] No nested ternary operators + +--- + +## Additional Resources + +- C Programming Language (K&R): The definitive C reference +- C Standard Library: https://en.cppreference.com/w/c +- CERT C Coding Standard: + https://wiki.sei.cmu.edu/confluence/display/c +- Modern C (Jens Gustedt): Free book on modern C practices + +**C Standard Recommendation**: Use C11 or C17 for modern +features while maintaining portability. diff --git a/code-simplifier/steering/core.md b/code-simplifier/steering/core.md new file mode 100644 index 0000000..bf1ca04 --- /dev/null +++ b/code-simplifier/steering/core.md @@ -0,0 +1,549 @@ +# Core Code Simplification Principles + +This guide covers universal code simplification principles +that apply across all programming languages and frameworks. + +## Table of Contents + +1. [Fundamental Principles](#fundamental-principles) +2. [Complexity Reduction](#complexity-reduction) +3. [Naming Conventions](#naming-conventions) +4. [Code Organization](#code-organization) +5. [Comments and Documentation](#comments-and-documentation) +6. [Common Patterns](#common-patterns) +7. [Anti-Patterns to Avoid](#anti-patterns-to-avoid) + +--- + +## Fundamental Principles + +### Preserve Functionality + +**Rule**: Never change what the code does, only how it does +it. + +**Guidelines**: + +- All inputs should produce the same outputs +- Side effects must remain identical +- Error handling behavior should be preserved +- Performance characteristics should not degrade + significantly + +**Verification**: + +- Run existing tests after refactoring +- Manually test edge cases +- Review behavior with original author if uncertain + +### Clarity Over Brevity + +**Rule**: Explicit code is better than compact code. + +**Examples**: + +```javascript +// Bad - too compact, hard to understand +const r = d + .filter((x) => x.a && x.b > 10) + .map((x) => ({ ...x, c: x.a * 2 })); + +// Good - clear and explicit +const activeItems = data.filter( + (item) => item.isActive && item.value > 10 +); +const enrichedItems = activeItems.map((item) => ({ + ...item, + calculatedValue: item.amount * 2 +})); +``` + +### Single Responsibility + +**Rule**: Each function/method should do one thing well. + +**Guidelines**: + +- If a function name contains "and", it might be doing too + much +- Functions should be easy to name descriptively +- Aim for functions that fit on one screen + +**Example**: + +```python +# Bad - doing too much +def process_user_data(user): + # Validate + if not user.email: + raise ValueError("Email required") + # Transform + user.email = user.email.lower() + # Save + db.save(user) + # Send email + send_welcome_email(user) + return user + +# Good - separated concerns +def validate_user(user): + if not user.email: + raise ValueError("Email required") + +def normalize_user_email(user): + user.email = user.email.lower() + return user + +def save_user(user): + return db.save(user) + +def process_new_user(user): + validate_user(user) + normalized_user = normalize_user_email(user) + saved_user = save_user(normalized_user) + send_welcome_email(saved_user) + return saved_user +``` + +--- + +## Complexity Reduction + +### Reduce Nesting + +**Problem**: Deep nesting makes code hard to follow. + +**Solution**: Use early returns (guard clauses). + +```javascript +// Bad - deep nesting +function processOrder(order) { + if (order) { + if (order.items.length > 0) { + if (order.customer) { + if (order.customer.isVerified) { + // process order + return processPayment(order); + } + } + } + } + return null; +} + +// Good - early returns +function processOrder(order) { + if (!order) return null; + if (order.items.length === 0) return null; + if (!order.customer) return null; + if (!order.customer.isVerified) return null; + + return processPayment(order); +} +``` + +### Eliminate Redundancy + +**Problem**: Duplicated code increases maintenance burden. + +**Solution**: Extract common logic into reusable functions. + +```python +# Bad - duplicated logic +def get_active_users(): + users = db.query(User).all() + return [u for u in users if u.is_active and not u.is_deleted] + +def get_active_admins(): + users = db.query(User).all() + return [u for u in users if u.is_active and not u.is_deleted and u.is_admin] + +# Good - extracted common logic +def get_active_users_base(): + users = db.query(User).all() + return [u for u in users if u.is_active and not u.is_deleted] + +def get_active_users(): + return get_active_users_base() + +def get_active_admins(): + return [u for u in get_active_users_base() if u.is_admin] +``` + +### Avoid Nested Ternaries + +**Problem**: Nested ternary operators are extremely hard to +read. + +**Solution**: Use explicit conditionals, match expressions, +or switch statements. + +```javascript +// Bad - nested ternary +const price = isPremium + ? isAnnual + ? 99 + : isMember + ? 15 + : 20 + : isAnnual + ? 120 + : 25; + +// Good - explicit if/else +let price; +if (isPremium) { + if (isAnnual) { + price = 99; + } else if (isMember) { + price = 15; + } else { + price = 20; + } +} else { + price = isAnnual ? 120 : 25; +} + +// Better - object lookup or switch +const pricingMatrix = { + "premium-annual": 99, + "premium-member": 15, + "premium-regular": 20, + "standard-annual": 120, + "standard-regular": 25 +}; + +const tier = isPremium ? "premium" : "standard"; +const type = + isPremium && isAnnual + ? "annual" + : isPremium && isMember + ? "member" + : isAnnual + ? "annual" + : "regular"; +const price = pricingMatrix[`${tier}-${type}`]; +``` + +--- + +## Naming Conventions + +### Variables and Functions + +**Guidelines**: + +- Use descriptive names that reveal intent +- Avoid abbreviations unless universally understood +- Use verbs for functions, nouns for variables +- Boolean variables should read like questions + +```python +# Bad +def calc(a, b): + return a * b * 0.2 + +usr = get_usr() +flg = True + +# Good +def calculate_discount(price, quantity): + DISCOUNT_RATE = 0.2 + return price * quantity * DISCOUNT_RATE + +current_user = get_current_user() +is_email_verified = True +``` + +### Constants + +**Guidelines**: + +- Use UPPER_SNAKE_CASE for true constants +- Group related constants together +- Consider using enums for related constant sets + +```javascript +// Bad +const x = 86400; +const y = 7; + +// Good +const SECONDS_PER_DAY = 86400; +const DAYS_PER_WEEK = 7; +const SECONDS_PER_WEEK = SECONDS_PER_DAY * DAYS_PER_WEEK; +``` + +### Classes and Types + +**Guidelines**: + +- Use PascalCase for class names +- Names should be nouns or noun phrases +- Avoid generic names like "Manager", "Helper", "Utility" + +```python +# Bad +class data_processor: + pass + +class Helper: + pass + +# Good +class OrderProcessor: + pass + +class EmailValidator: + pass +``` + +--- + +## Code Organization + +### Function Length + +**Guideline**: Functions should be short and focused. + +**Rules of Thumb**: + +- Aim for functions under 20-30 lines +- If you need to scroll, consider splitting +- Each function should do one thing at one level of + abstraction + +### File Organization + +**Guidelines**: + +- Group related functionality together +- Keep files focused on a single concern +- Use clear directory structure + +``` +# Good structure +src/ + users/ + user-service.js + user-repository.js + user-validator.js + orders/ + order-service.js + order-repository.js + order-validator.js +``` + +### Dependency Management + +**Guidelines**: + +- Minimize dependencies between modules +- Depend on abstractions, not concretions +- Avoid circular dependencies + +--- + +## Comments and Documentation + +### When to Comment + +**Do comment**: + +- Why something is done (not what or how) +- Complex algorithms or business logic +- Workarounds for bugs or limitations +- Public APIs and interfaces + +**Don't comment**: + +- Obvious code that explains itself +- What the code does (the code should show this) +- Commented-out code (use version control instead) + +```javascript +// Bad - obvious comment +// Increment counter by 1 +counter++; + +// Get user by ID +const user = getUserById(id); + +// Good - explains why +// Use exponential backoff to avoid overwhelming the API +// during high-traffic periods +await retryWithBackoff(apiCall, { maxRetries: 5 }); + +// Cache results for 5 minutes to reduce database load +// while keeping data reasonably fresh for users +const cachedResult = cache.get(key, { ttl: 300 }); +``` + +### Self-Documenting Code + +**Prefer code that explains itself**: + +```python +# Bad - needs comments +# Check if user can access resource +if u.r == 'admin' or (u.r == 'user' and r.o == u.id): + # allow access + pass + +# Good - self-documenting +def user_can_access_resource(user, resource): + is_admin = user.role == 'admin' + is_owner = user.role == 'user' and resource.owner_id == user.id + return is_admin or is_owner + +if user_can_access_resource(current_user, requested_resource): + # grant access + pass +``` + +--- + +## Common Patterns + +### Replace Magic Numbers + +```javascript +// Bad +if (user.age >= 18 && user.age < 65) { + applyDiscount(0.15); +} + +// Good +const ADULT_AGE = 18; +const SENIOR_AGE = 65; +const STANDARD_DISCOUNT = 0.15; + +if (user.age >= ADULT_AGE && user.age < SENIOR_AGE) { + applyDiscount(STANDARD_DISCOUNT); +} +``` + +### Extract Complex Conditions + +```python +# Bad +if (user.subscription_type == 'premium' and user.payment_status == 'current' + and user.account_age_days > 30 and not user.has_violations): + grant_feature_access() + +# Good +def user_qualifies_for_premium_features(user): + has_premium_subscription = user.subscription_type == 'premium' + payment_is_current = user.payment_status == 'current' + account_is_established = user.account_age_days > 30 + has_good_standing = not user.has_violations + + return (has_premium_subscription and payment_is_current + and account_is_established and has_good_standing) + +if user_qualifies_for_premium_features(user): + grant_feature_access() +``` + +### Use Data Structures + +```javascript +// Bad - long if/else chain +function getShippingCost(country) { + if (country === "US") return 5; + if (country === "CA") return 7; + if (country === "UK") return 10; + if (country === "AU") return 12; + return 15; +} + +// Good - data structure +const SHIPPING_COSTS = { + US: 5, + CA: 7, + UK: 10, + AU: 12, + DEFAULT: 15 +}; + +function getShippingCost(country) { + return ( + SHIPPING_COSTS[country] || SHIPPING_COSTS.DEFAULT + ); +} +``` + +--- + +## Anti-Patterns to Avoid + +### Premature Optimization + +**Problem**: Optimizing before you know where the +bottlenecks are. + +**Solution**: Write clear code first, optimize only when +profiling shows a need. + +### Over-Engineering + +**Problem**: Adding complexity for hypothetical future +needs. + +**Solution**: Implement what you need now. YAGNI (You Aren't +Gonna Need It). + +### God Objects + +**Problem**: Classes or modules that know too much or do too +much. + +**Solution**: Split responsibilities into focused, cohesive +units. + +### Clever Code + +**Problem**: Code that's hard to understand because it's too +"clever". + +**Solution**: Prefer straightforward solutions over clever +tricks. + +```python +# Bad - too clever +result = [x for x in (y for y in data if y) if x % 2] + +# Good - clear and explicit +non_empty_items = [item for item in data if item] +odd_numbers = [num for num in non_empty_items if num % 2 != 0] +``` + +### Inconsistent Style + +**Problem**: Mixing different coding styles in the same +codebase. + +**Solution**: Follow a consistent style guide and use +automated formatters. + +--- + +## Simplification Checklist + +Before considering your simplification complete, verify: + +- [ ] Functionality is preserved (all tests pass) +- [ ] Code is more readable than before +- [ ] Nesting depth is reduced where possible +- [ ] Variable and function names are clear +- [ ] Redundant code is eliminated +- [ ] Complex conditions are extracted +- [ ] Magic numbers are replaced with named constants +- [ ] Comments explain "why", not "what" +- [ ] Code follows consistent style +- [ ] No over-simplification that reduces clarity + +--- + +**Remember**: The goal is maintainable code that's easy to +understand, not the shortest possible code. diff --git a/code-simplifier/steering/csharp.md b/code-simplifier/steering/csharp.md new file mode 100644 index 0000000..b7328e8 --- /dev/null +++ b/code-simplifier/steering/csharp.md @@ -0,0 +1,777 @@ +# C# Code Simplification Guide + +This guide covers C#-specific simplification patterns and +best practices for writing clean, maintainable C# code. + +## Table of Contents + +1. [C# Style and Conventions](#csharp-style-and-conventions) +2. [LINQ and Collections](#linq-and-collections) +3. [Async/Await](#asyncawait) +4. [Null Safety](#null-safety) +5. [Properties and Expression Bodies](#properties-and-expression-bodies) +6. [Pattern Matching](#pattern-matching) +7. [Error Handling](#error-handling) +8. [Dependency Injection and SOLID](#dependency-injection-and-solid) + +--- + +## C# Style and Conventions + +### Naming Conventions + +```csharp +// Classes, interfaces, methods - PascalCase +public class UserService +{ + // Interface prefix with 'I' + private readonly IUserRepository _repository; + + // Private fields - _camelCase with underscore + private readonly ILogger _logger; + + // Public properties - PascalCase + public string UserName { get; set; } + + // Local variables and parameters - camelCase + public void ProcessUser(int userId) + { + var userName = GetUserName(userId); + // ... + } + + // Constants - PascalCase + private const int MaxRetries = 3; + + // Async methods - suffix with 'Async' + public async Task GetUserAsync(int id) + { + // ... + } +} +``` + +### File Organization + +```csharp +// One class per file (generally) +// File name matches class name: UserService.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +// Third-party using statements +using Microsoft.Extensions.Logging; + +// Local using statements +using MyApp.Models; +using MyApp.Repositories; + +namespace MyApp.Services +{ + public class UserService + { + // Fields + private readonly IUserRepository _repository; + + // Constructor + public UserService(IUserRepository repository) + { + _repository = repository; + } + + // Methods + public async Task GetUserAsync(int id) + { + // implementation + } + } +} +``` + +--- + +## LINQ and Collections + +### Use LINQ for Collection Operations + +```csharp +var numbers = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + +// Bad - manual loop +var evenNumbers = new List(); +foreach (var num in numbers) +{ + if (num % 2 == 0) + { + evenNumbers.Add(num); + } +} + +// Good - LINQ +var evenNumbers = numbers.Where(n => n % 2 == 0).ToList(); + +// Method chaining +var result = numbers + .Where(n => n % 2 == 0) + .Select(n => n * n) + .OrderByDescending(n => n) + .Take(3) + .ToList(); +``` + +### Query Syntax vs Method Syntax + +```csharp +var users = GetUsers(); + +// Query syntax - good for complex queries with multiple from clauses +var query1 = from user in users + where user.IsActive + orderby user.Name + select user.Email; + +// Method syntax - more common and flexible +var query2 = users + .Where(u => u.IsActive) + .OrderBy(u => u.Name) + .Select(u => u.Email); + +// Use method syntax for most cases +// Use query syntax when it improves readability (joins, multiple from) +``` + +### Avoid Multiple Enumeration + +```csharp +// Bad - enumerates twice +var activeUsers = users.Where(u => u.IsActive); +var count = activeUsers.Count(); +var firstUser = activeUsers.First(); // Enumerates again! + +// Good - materialize once +var activeUsers = users.Where(u => u.IsActive).ToList(); +var count = activeUsers.Count; +var firstUser = activeUsers.First(); + +// Or use Any() instead of Count() when checking existence +if (users.Any(u => u.IsActive)) +{ + // Better than users.Count(u => u.IsActive) > 0 +} +``` + +### Collection Initializers + +```csharp +// Bad - verbose +var list = new List(); +list.Add("apple"); +list.Add("banana"); +list.Add("cherry"); + +// Good - collection initializer +var list = new List { "apple", "banana", "cherry" }; + +// Dictionary initializer +var dict = new Dictionary +{ + ["apple"] = 1, + ["banana"] = 2, + ["cherry"] = 3 +}; + +// Or with Add syntax +var dict = new Dictionary +{ + { "apple", 1 }, + { "banana", 2 }, + { "cherry", 3 } +}; +``` + +--- + +## Async/Await + +### Always Use Async/Await for I/O Operations + +```csharp +// Bad - blocking call +public User GetUser(int id) +{ + var response = httpClient.GetAsync($"/users/{id}").Result; // Blocks! + return response.Content.ReadAsAsync().Result; +} + +// Good - async all the way +public async Task GetUserAsync(int id) +{ + var response = await httpClient.GetAsync($"/users/{id}"); + return await response.Content.ReadAsAsync(); +} +``` + +### ConfigureAwait for Library Code + +```csharp +// In library code (not UI or ASP.NET Core) +public async Task ReadFileAsync(string path) +{ + using var reader = new StreamReader(path); + return await reader.ReadToEndAsync().ConfigureAwait(false); +} + +// In ASP.NET Core and UI code, ConfigureAwait(false) is not needed +public async Task GetUser(int id) +{ + var user = await _userService.GetUserAsync(id); + return Ok(user); +} +``` + +### Parallel Async Operations + +```csharp +// Bad - sequential execution +var user = await GetUserAsync(userId); +var orders = await GetOrdersAsync(userId); +var preferences = await GetPreferencesAsync(userId); + +// Good - parallel execution +var userTask = GetUserAsync(userId); +var ordersTask = GetOrdersAsync(userId); +var preferencesTask = GetPreferencesAsync(userId); + +await Task.WhenAll(userTask, ordersTask, preferencesTask); + +var user = userTask.Result; +var orders = ordersTask.Result; +var preferences = preferencesTask.Result; + +// Or with tuple deconstruction +var (user, orders, preferences) = await ( + GetUserAsync(userId), + GetOrdersAsync(userId), + GetPreferencesAsync(userId) +); +``` + +### Avoid Async Void + +```csharp +// Bad - async void (only for event handlers) +public async void ProcessData() +{ + await DoSomethingAsync(); +} + +// Good - async Task +public async Task ProcessDataAsync() +{ + await DoSomethingAsync(); +} + +// Exception: Event handlers must be async void +private async void Button_Click(object sender, EventArgs e) +{ + try + { + await ProcessDataAsync(); + } + catch (Exception ex) + { + // Handle exception + } +} +``` + +--- + +## Null Safety + +### Nullable Reference Types (C# 8.0+) + +```csharp +// Enable in .csproj +// enable + +// Non-nullable by default +public class User +{ + public string Name { get; set; } // Warning if not initialized + public string? MiddleName { get; set; } // Nullable + + public User(string name) + { + Name = name; // Must be set + } +} +``` + +### Null-Coalescing Operators + +```csharp +// Null-coalescing operator (??) +string name = user.Name ?? "Unknown"; + +// Null-coalescing assignment (??=) +if (cache == null) +{ + cache = new Cache(); +} +// Better +cache ??= new Cache(); + +// Null-conditional operator (?.) +var length = user?.Name?.Length; + +// Null-conditional with null-coalescing +var length = user?.Name?.Length ?? 0; +``` + +### Pattern Matching for Null Checks + +```csharp +// Bad - traditional null check +if (user != null && user.IsActive) +{ + ProcessUser(user); +} + +// Good - pattern matching (C# 9.0+) +if (user is { IsActive: true }) +{ + ProcessUser(user); +} + +// Not null pattern +if (user is not null) +{ + ProcessUser(user); +} +``` + +--- + +## Properties and Expression Bodies + +### Auto-Properties + +```csharp +// Bad - unnecessary backing field +private string _name; +public string Name +{ + get { return _name; } + set { _name = value; } +} + +// Good - auto-property +public string Name { get; set; } + +// Read-only auto-property +public string Name { get; } + +// Init-only property (C# 9.0+) +public string Name { get; init; } + +// Property with default value +public int Age { get; set; } = 0; +``` + +### Expression-Bodied Members + +```csharp +// Properties +public string FullName => $"{FirstName} {LastName}"; + +// Methods +public int Add(int a, int b) => a + b; + +// Constructors +public User(string name) => Name = name; + +// Finalizers +~User() => Cleanup(); + +// Indexers +public string this[int index] => _items[index]; +``` + +### Computed Properties + +```csharp +public class Order +{ + public List Items { get; set; } + + // Bad - method for simple calculation + public decimal GetTotal() + { + return Items.Sum(i => i.Price * i.Quantity); + } + + // Good - computed property + public decimal Total => Items.Sum(i => i.Price * i.Quantity); + + // With caching if expensive + private decimal? _cachedTotal; + public decimal Total + { + get + { + if (_cachedTotal == null) + { + _cachedTotal = Items.Sum(i => i.Price * i.Quantity); + } + return _cachedTotal.Value; + } + } +} +``` + +--- + +## Pattern Matching + +### Switch Expressions (C# 8.0+) + +```csharp +// Bad - traditional switch +string GetStatusMessage(OrderStatus status) +{ + switch (status) + { + case OrderStatus.Pending: + return "Order is pending"; + case OrderStatus.Processing: + return "Order is being processed"; + case OrderStatus.Completed: + return "Order completed"; + case OrderStatus.Cancelled: + return "Order cancelled"; + default: + return "Unknown status"; + } +} + +// Good - switch expression +string GetStatusMessage(OrderStatus status) => status switch +{ + OrderStatus.Pending => "Order is pending", + OrderStatus.Processing => "Order is being processed", + OrderStatus.Completed => "Order completed", + OrderStatus.Cancelled => "Order cancelled", + _ => "Unknown status" +}; +``` + +### Type Patterns + +```csharp +// Bad - type checking and casting +if (obj is string) +{ + string str = (string)obj; + Console.WriteLine(str.Length); +} + +// Good - pattern matching with declaration +if (obj is string str) +{ + Console.WriteLine(str.Length); +} + +// Switch with type patterns +string Describe(object obj) => obj switch +{ + int i => $"Integer: {i}", + string s => $"String of length {s.Length}", + User u => $"User: {u.Name}", + null => "null", + _ => "Unknown type" +}; +``` + +### Property Patterns + +```csharp +// Check properties in patterns +string GetDiscount(User user) => user switch +{ + { IsPremium: true, OrderCount: > 10 } => "20% off", + { IsPremium: true } => "10% off", + { OrderCount: > 5 } => "5% off", + _ => "No discount" +}; + +// Nested property patterns +string GetShippingCost(Order order) => order switch +{ + { ShippingAddress: { Country: "US" }, Total: > 100 } => "Free", + { ShippingAddress: { Country: "US" } } => "$10", + { ShippingAddress: { Country: "CA" } } => "$15", + _ => "$25" +}; +``` + +### Relational Patterns (C# 9.0+) + +```csharp +string GetTemperatureDescription(int temp) => temp switch +{ + < 0 => "Freezing", + >= 0 and < 10 => "Cold", + >= 10 and < 20 => "Cool", + >= 20 and < 30 => "Warm", + >= 30 => "Hot" +}; +``` + +--- + +## Error Handling + +### Use Specific Exceptions + +```csharp +// Bad - generic exception +if (user == null) +{ + throw new Exception("User not found"); +} + +// Good - specific exception +if (user == null) +{ + throw new ArgumentNullException(nameof(user)); +} + +// Custom exceptions +public class UserNotFoundException : Exception +{ + public int UserId { get; } + + public UserNotFoundException(int userId) + : base($"User with ID {userId} not found") + { + UserId = userId; + } +} +``` + +### Exception Filters + +```csharp +// Use when clauses to filter exceptions +try +{ + await ProcessDataAsync(); +} +catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound) +{ + // Handle 404 specifically +} +catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized) +{ + // Handle 401 specifically +} +catch (HttpRequestException ex) +{ + // Handle other HTTP errors +} +``` + +### Using Statements + +```csharp +// Bad - manual disposal +FileStream file = new FileStream("data.txt", FileMode.Open); +try +{ + // Use file +} +finally +{ + file.Dispose(); +} + +// Good - using statement +using (var file = new FileStream("data.txt", FileMode.Open)) +{ + // Use file +} // Automatically disposed + +// Better - using declaration (C# 8.0+) +using var file = new FileStream("data.txt", FileMode.Open); +// Use file +// Automatically disposed at end of scope +``` + +--- + +## Dependency Injection and SOLID + +### Constructor Injection + +```csharp +// Bad - tight coupling +public class UserService +{ + private readonly UserRepository _repository = new UserRepository(); + + public User GetUser(int id) + { + return _repository.GetById(id); + } +} + +// Good - dependency injection +public class UserService +{ + private readonly IUserRepository _repository; + + public UserService(IUserRepository repository) + { + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + } + + public User GetUser(int id) + { + return _repository.GetById(id); + } +} +``` + +### Interface Segregation + +```csharp +// Bad - fat interface +public interface IUserService +{ + User GetUser(int id); + void CreateUser(User user); + void UpdateUser(User user); + void DeleteUser(int id); + List SearchUsers(string query); + void SendEmail(int userId, string message); + void GenerateReport(int userId); +} + +// Good - segregated interfaces +public interface IUserReader +{ + User GetUser(int id); + List SearchUsers(string query); +} + +public interface IUserWriter +{ + void CreateUser(User user); + void UpdateUser(User user); + void DeleteUser(int id); +} + +public interface IUserNotifier +{ + void SendEmail(int userId, string message); +} +``` + +### Single Responsibility + +```csharp +// Bad - multiple responsibilities +public class UserService +{ + public void RegisterUser(User user) + { + // Validate user + if (string.IsNullOrEmpty(user.Email)) + throw new ValidationException("Email required"); + + // Save to database + _context.Users.Add(user); + _context.SaveChanges(); + + // Send email + var smtp = new SmtpClient(); + smtp.Send(new MailMessage("from@example.com", user.Email, "Welcome", "Welcome!")); + + // Log activity + File.AppendAllText("log.txt", $"User registered: {user.Email}"); + } +} + +// Good - separated concerns +public class UserService +{ + private readonly IUserRepository _repository; + private readonly IUserValidator _validator; + private readonly IEmailService _emailService; + private readonly ILogger _logger; + + public UserService( + IUserRepository repository, + IUserValidator validator, + IEmailService emailService, + ILogger logger) + { + _repository = repository; + _validator = validator; + _emailService = emailService; + _logger = logger; + } + + public async Task RegisterUserAsync(User user) + { + _validator.Validate(user); + + await _repository.AddAsync(user); + + await _emailService.SendWelcomeEmailAsync(user.Email); + + _logger.LogInformation("User registered: {Email}", user.Email); + } +} +``` + +--- + +## C# Simplification Checklist + +- [ ] Following C# naming conventions (PascalCase, + camelCase) +- [ ] Using LINQ for collection operations +- [ ] Async/await for I/O operations +- [ ] Nullable reference types enabled +- [ ] Null-coalescing and null-conditional operators +- [ ] Auto-properties instead of backing fields +- [ ] Expression-bodied members where appropriate +- [ ] Switch expressions over traditional switch +- [ ] Pattern matching for type checks +- [ ] Specific exception types +- [ ] Using statements for IDisposable +- [ ] Constructor injection for dependencies +- [ ] Interface segregation +- [ ] Single responsibility principle +- [ ] No nested ternary operators (use switch expressions) + +--- + +## Additional Resources + +- C# Documentation: + https://docs.microsoft.com/en-us/dotnet/csharp/ +- .NET API Browser: + https://docs.microsoft.com/en-us/dotnet/api/ +- C# Coding Conventions: + https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions +- Dependency Injection in .NET: + https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection + +**C# Version Recommendation**: Use C# 10+ with .NET 6+ for +latest features and performance improvements. diff --git a/code-simplifier/steering/java-spring.md b/code-simplifier/steering/java-spring.md new file mode 100644 index 0000000..23f8df6 --- /dev/null +++ b/code-simplifier/steering/java-spring.md @@ -0,0 +1,435 @@ +# Spring Boot Code Simplification Guide + +This guide covers Spring Boot-specific patterns, conventions, +and best practices for writing clean, maintainable code. + +## Table of Contents + +1. [Spring Boot Conventions](#spring-boot-conventions) +2. [Dependency Injection](#dependency-injection) +3. [REST Controllers](#rest-controllers) +4. [Service Layer Patterns](#service-layer-patterns) +5. [JPA/Hibernate Best Practices](#jpahibernate-best-practices) +6. [Configuration](#configuration) +7. [Testing](#testing) +8. [Common Spring Annotations](#common-spring-annotations) + +--- + +## Spring Boot Conventions + +### Directory Structure + +Keep packages aligned with features and layers: + +``` +src/main/java/com/example/app +├── Application.java +├── config/ +├── controller/ +├── service/ +├── repository/ +├── domain/ +├── dto/ +└── mapper/ +``` + +### Naming Conventions + +```java +// Controllers - resource focused, Controller suffix +OrderController, UserController + +// Services - behavior focused, Service suffix +OrderService, BillingService + +// Repositories - data access, Repository suffix +OrderRepository, UserRepository + +// Entities - singular +Order, User, OrderItem + +// DTOs - explicit type role +CreateOrderRequest, OrderResponse, UserSummary +``` + +--- + +## Dependency Injection + +### Prefer Constructor Injection + +```java +// Bad - field injection hides dependencies, hard to test +@Service +public class OrderService { + @Autowired + private OrderRepository orderRepository; +} + +// Good - constructor injection is explicit and test-friendly +@Service +public class OrderService { + private final OrderRepository orderRepository; + + public OrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } +} +``` + +### Use Interfaces for Swappable Implementations + +```java +// Good - interface for easy testing/mocking +public interface PaymentGateway { + PaymentResult charge(PaymentRequest request); +} + +@Service +public class StripePaymentGateway implements PaymentGateway { + @Override + public PaymentResult charge(PaymentRequest request) { + return PaymentResult.success(); + } +} +``` + +--- + +## REST Controllers + +### Thin Controllers + +```java +// Bad - fat controller with validation, logic, and persistence +@RestController +@RequestMapping("/orders") +public class OrderController { + private final OrderRepository orderRepository; + + public OrderController(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + @PostMapping + public ResponseEntity create(@RequestBody CreateOrderRequest request) { + if (request.items() == null || request.items().isEmpty()) { + return ResponseEntity.badRequest().build(); + } + + Order order = new Order(); + order.setStatus("PENDING"); + order.setTotal(request.items().stream() + .mapToDouble(i -> i.price() * i.quantity()) + .sum()); + + return ResponseEntity.status(HttpStatus.CREATED).body(orderRepository.save(order)); + } +} + +// Good - controller delegates to service, validation in DTO +@RestController +@RequestMapping("/orders") +public class OrderController { + private final OrderService orderService; + + public OrderController(OrderService orderService) { + this.orderService = orderService; + } + + @PostMapping + public ResponseEntity create( + @Valid @RequestBody CreateOrderRequest request + ) { + OrderResponse response = orderService.createOrder(request); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } +} +``` + +### DTOs Over Entities + +```java +// Bad - exposing entity directly +@GetMapping("/{id}") +public Order getOrder(@PathVariable Long id) { + return orderService.getOrderEntity(id); +} + +// Good - map to response DTO +@GetMapping("/{id}") +public OrderResponse getOrder(@PathVariable Long id) { + return orderService.getOrderResponse(id); +} +``` + +--- + +## Service Layer Patterns + +### Service Composition + +```java +// Good - orchestration in service layer +@Service +public class OrderService { + private final OrderRepository orderRepository; + private final PricingService pricingService; + private final NotificationService notificationService; + + public OrderService( + OrderRepository orderRepository, + PricingService pricingService, + NotificationService notificationService + ) { + this.orderRepository = orderRepository; + this.pricingService = pricingService; + this.notificationService = notificationService; + } + + public OrderResponse createOrder(CreateOrderRequest request) { + Money total = pricingService.calculateTotal(request.items()); + Order order = Order.createPending(total); + Order saved = orderRepository.save(order); + notificationService.sendOrderConfirmation(saved); + return OrderResponse.from(saved); + } +} +``` + +### Transactions + +```java +// Bad - missing transactional boundary for multi-step updates +public void fulfillOrder(Long orderId) { + Order order = orderRepository.findById(orderId).orElseThrow(); + order.markPaid(); + inventoryService.reserve(order.getItems()); + orderRepository.save(order); +} + +// Good - transactional boundary around workflow +@Transactional +public void fulfillOrder(Long orderId) { + Order order = orderRepository.findById(orderId).orElseThrow(); + order.markPaid(); + inventoryService.reserve(order.getItems()); + orderRepository.save(order); +} +``` + +--- + +## JPA/Hibernate Best Practices + +### Entity Design + +```java +// Good - use immutable identifiers and encapsulate changes +@Entity +public class Order { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + private OrderStatus status; + + protected Order() { + } + + public static Order createPending(Money total) { + Order order = new Order(); + order.status = OrderStatus.PENDING; + order.total = total; + return order; + } + + public void markPaid() { + this.status = OrderStatus.PAID; + } +} +``` + +### Fetching Strategy + +```java +// Bad - N+1 queries due to lazy loading in loops +List orders = orderRepository.findAll(); +for (Order order : orders) { + order.getItems().size(); +} + +// Good - fetch required relationships with join fetch +@Query("select o from Order o join fetch o.items where o.id = :id") +Optional findWithItems(@Param("id") Long id); +``` + +### Avoid Bidirectional Overuse + +```java +// Bad - heavy bidirectional mapping everywhere +@OneToMany(mappedBy = "order") +private List items = new ArrayList<>(); + +// Good - use unidirectional where simpler +@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) +@JoinColumn(name = "order_id") +private List items = new ArrayList<>(); +``` + +### Pagination and Sorting + +```java +// Bad - loading all rows into memory +List orders = orderRepository.findAll(); + +// Good - use pagination +Page orders = orderRepository.findAll(PageRequest.of(0, 20, Sort.by("createdAt").descending())); +``` + +--- + +## Configuration + +### Use Configuration Properties + +```java +// Bad - scattered @Value usage +@Value("${billing.currency}") +private String currency; + +// Good - strongly typed config +@ConfigurationProperties(prefix = "billing") +public class BillingProperties { + private String currency; + private int retryCount; + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public int getRetryCount() { + return retryCount; + } + + public void setRetryCount(int retryCount) { + this.retryCount = retryCount; + } +} +``` + +### Profiles + +```java +// application.yml +spring: + profiles: + active: dev + +// application-dev.yml +logging: + level: + root: DEBUG +``` + +--- + +## Testing + +### Slice Tests for Controllers + +```java +// Bad - full context for simple controller test +@SpringBootTest +@AutoConfigureMockMvc +class OrderControllerTest { +} + +// Good - controller slice test +@WebMvcTest(OrderController.class) +class OrderControllerTest { + @Autowired + private MockMvc mockMvc; + + @MockBean + private OrderService orderService; +} +``` + +### Repository Tests + +```java +@DataJpaTest +class OrderRepositoryTest { + @Autowired + private OrderRepository orderRepository; +} +``` + +### Service Tests + +```java +// Prefer unit tests with mocks for services +class OrderServiceTest { + private final OrderRepository orderRepository = mock(OrderRepository.class); + private final PricingService pricingService = mock(PricingService.class); + private final NotificationService notificationService = mock(NotificationService.class); + private final OrderService orderService = new OrderService( + orderRepository, + pricingService, + notificationService + ); +} +``` + +--- + +## Common Spring Annotations + +```java +@SpringBootApplication // Bootstraps the app +@RestController // REST controller +@RequestMapping // Base path for controller +@GetMapping, @PostMapping, @PutMapping, @DeleteMapping // HTTP methods +@Service // Service layer +@Repository // Data access layer +@Component // Generic component +@Configuration // Configuration class +@Bean // Bean factory method +@Autowired // Dependency injection (prefer constructor) +@Transactional // Transaction boundary +@Valid // Bean validation +@ConfigurationProperties // Typed config +``` + +--- + +## Spring Boot Simplification Checklist + +- [ ] Controllers are thin and delegate to services +- [ ] Constructor injection used everywhere +- [ ] DTOs used for API requests and responses +- [ ] Transactions wrap multi-step updates +- [ ] JPA queries avoid N+1 problems +- [ ] Pagination for large datasets +- [ ] Configuration properties are typed +- [ ] Tests use slices where appropriate +- [ ] Entities encapsulate state changes +- [ ] Avoid overusing bidirectional mappings + +--- + +## Additional Resources + +- Spring Boot Documentation: https://spring.io/projects/spring-boot +- Spring Data JPA Reference: https://spring.io/projects/spring-data-jpa +- Spring Guides: https://spring.io/guides + +**Spring Boot Version Recommendation**: Use Spring Boot 3.x +with Java 17+ for modern features and support. diff --git a/code-simplifier/steering/java.md b/code-simplifier/steering/java.md new file mode 100644 index 0000000..e1b90af --- /dev/null +++ b/code-simplifier/steering/java.md @@ -0,0 +1,571 @@ +# Java Code Simplification Guide + +This guide covers Java-specific simplification patterns +and best practices for writing clear, modern Java. + +## Table of Contents + +1. [Java Naming and Style](#java-naming-and-style) +2. [Modern Java Features](#modern-java-features) +3. [Streams, Lambdas, and Optional](#streams-lambdas-and-optional) +4. [Collections](#collections) +5. [Exception Handling](#exception-handling) +6. [Generics](#generics) +7. [Design Patterns](#design-patterns) +8. [Best Practices](#best-practices) + +--- + +## Java Naming and Style + +### Naming Conventions + +```java +// Classes - PascalCase +public class OrderService { +} + +// Interfaces - PascalCase, use adjectives if possible +public interface SerializableReport { +} + +// Methods and variables - lowerCamelCase +public int calculateTotal(List items) { + return 0; +} + +// Constants - UPPER_SNAKE_CASE +public static final int MAX_RETRIES = 3; + +// Enums - PascalCase with UPPER_SNAKE_CASE constants +public enum Status { + PENDING, + COMPLETED +} +``` + +### Package and Import Organization + +```java +// Packages - lower case, reverse domain +package com.acme.orders.service; + +// Standard library imports +import java.time.Instant; +import java.util.List; + +// Third-party imports +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// Local application imports +import com.acme.orders.model.Order; +import com.acme.orders.repository.OrderRepository; +``` + +### Formatting and Readability + +```java +// Bad - long line +Order order = orderService.create(user, items, address, paymentMethod, discountCode, notes); + +// Good - break into multiple lines +Order order = orderService.create( + user, + items, + address, + paymentMethod, + discountCode, + notes +); + +// Prefer early returns for clarity +public Optional findActiveOrder(User user) { + if (user == null) { + return Optional.empty(); + } + return orderRepository.findActiveByUserId(user.getId()); +} +``` + +--- + +## Modern Java Features + +### Records (Java 16+) + +```java +// Bad - verbose data holder +public final class Address { + private final String street; + private final String city; + + public Address(String street, String city) { + this.street = street; + this.city = city; + } + + public String getStreet() { return street; } + public String getCity() { return city; } +} + +// Good - record for simple data +public record Address(String street, String city) {} +``` + +### Switch Expressions (Java 14+) + +```java +// Bad - fall-through switch +int discount; +switch (tier) { + case GOLD: + discount = 20; + break; + case SILVER: + discount = 10; + break; + default: + discount = 0; +} + +// Good - switch expression +int discount = switch (tier) { + case GOLD -> 20; + case SILVER -> 10; + default -> 0; +}; +``` + +### Text Blocks (Java 15+) + +```java +// Bad - hard-to-read string +String json = "{\n" + + " \"name\": \"Alice\",\n" + + " \"active\": true\n" + + "}"; + +// Good - text block +String json = """ + { + "name": "Alice", + "active": true + } + """; +``` + +### Sealed Classes (Java 17+) + +```java +// Good - sealed hierarchy with explicit variants +public sealed interface Payment permits CardPayment, BankTransfer {} + +public record CardPayment(String cardId) implements Payment {} +public record BankTransfer(String iban) implements Payment {} +``` + +--- + +## Streams, Lambdas, and Optional + +### Streams vs Loops + +```java +// Bad - verbose loop +List activeEmails = new ArrayList<>(); +for (User user : users) { + if (user.isActive()) { + activeEmails.add(user.getEmail().toLowerCase()); + } +} + +// Good - stream pipeline +List activeEmails = users.stream() + .filter(User::isActive) + .map(User::getEmail) + .map(String::toLowerCase) + .toList(); +``` + +### Avoid Overusing Streams + +```java +// Bad - stream for simple case +boolean hasAdmin = users.stream() + .anyMatch(user -> user.getRole() == Role.ADMIN); + +// Good - for-each is fine when simple +boolean hasAdmin = false; +for (User user : users) { + if (user.getRole() == Role.ADMIN) { + hasAdmin = true; + break; + } +} +``` + +### Optional Usage + +```java +// Bad - Optional used as field type +public class UserProfile { + private Optional nickname; +} + +// Good - Optional for return types +public Optional findByEmail(String email) { + return userRepository.findByEmail(email); +} + +// Avoid get() without a fallback +String name = user.findNickname() + .orElse("Anonymous"); + +// Map and flatMap +String city = user.findAddress() + .map(Address::city) + .orElse("Unknown"); +``` + +### Method References and Lambdas + +```java +// Bad - unnecessary lambda +users.forEach(user -> audit.log(user)); + +// Good - method reference +users.forEach(audit::log); +``` + +--- + +## Collections + +### Prefer Interfaces + +```java +// Bad - concrete type everywhere +ArrayList orders = new ArrayList<>(); + +// Good - program to interface +List orders = new ArrayList<>(); +``` + +### Use Unmodifiable Collections + +```java +// Bad - returns mutable list +public List getTags() { + return tags; +} + +// Good - return unmodifiable view or copy +public List getTags() { + return List.copyOf(tags); +} +``` + +### Choose the Right Collection + +```java +// Bad - using List for lookup +boolean exists = false; +for (String code : codes) { + if (code.equals(target)) { + exists = true; + break; + } +} + +// Good - use Set for lookup +Set codeSet = new HashSet<>(codes); +boolean exists = codeSet.contains(target); +``` + +### Map Operations + +```java +// Bad - manual null checks +Integer count = counts.get(key); +if (count == null) { + counts.put(key, 1); +} else { + counts.put(key, count + 1); +} + +// Good - use Map helpers +counts.merge(key, 1, Integer::sum); +``` + +--- + +## Exception Handling + +### Specific Exceptions + +```java +// Bad - catching Exception +try { + process(order); +} catch (Exception e) { + log.error("Failed", e); +} + +// Good - specific exceptions +try { + process(order); +} catch (ValidationException e) { + log.warn("Invalid order: {}", e.getMessage()); +} catch (IOException e) { + log.error("I/O error", e); +} +``` + +### Try-with-Resources + +```java +// Bad - manual close +BufferedReader reader = new BufferedReader(new FileReader(path)); +try { + return reader.readLine(); +} finally { + reader.close(); +} + +// Good - try-with-resources +try (BufferedReader reader = new BufferedReader(new FileReader(path))) { + return reader.readLine(); +} +``` + +### Custom Exceptions + +```java +// Good - domain-specific exception +public class OrderNotFoundException extends RuntimeException { + public OrderNotFoundException(String orderId) { + super("Order not found: " + orderId); + } +} + +public Order getOrder(String orderId) { + return orderRepository.findById(orderId) + .orElseThrow(() -> new OrderNotFoundException(orderId)); +} +``` + +--- + +## Generics + +### Avoid Raw Types + +```java +// Bad - raw types +List items = new ArrayList(); +items.add("name"); +items.add(42); + +// Good - parameterized types +List names = new ArrayList<>(); +names.add("Alice"); +``` + +### Bounded Type Parameters + +```java +// Bad - too generic +public static T max(T a, T b, Comparator comparator) { + return comparator.compare(a, b) >= 0 ? a : b; +} + +// Good - bounded to Comparable +public static > T max(T a, T b) { + return a.compareTo(b) >= 0 ? a : b; +} +``` + +### Wildcards + +```java +// Bad - overly strict parameter +public void printUsers(List users) { + users.forEach(System.out::println); +} + +// Good - accept subtypes +public void printUsers(List users) { + users.forEach(System.out::println); +} +``` + +--- + +## Design Patterns + +### Builder for Complex Objects + +```java +// Bad - many constructor params +User user = new User( + id, + name, + email, + phone, + address, + preferences +); + +// Good - builder +User user = User.builder() + .id(id) + .name(name) + .email(email) + .phone(phone) + .address(address) + .preferences(preferences) + .build(); +``` + +### Strategy for Algorithms + +```java +// Bad - conditional logic spread around +public int calculateDiscount(Order order) { + if (order.isVip()) { + return 20; + } + if (order.isEmployee()) { + return 30; + } + return 0; +} + +// Good - strategy map +public interface DiscountStrategy { + int discountFor(Order order); +} + +Map strategies = Map.of( + OrderType.VIP, order -> 20, + OrderType.EMPLOYEE, order -> 30, + OrderType.STANDARD, order -> 0 +); +``` + +### Factory for Object Creation + +```java +// Bad - scattered new calls +Payment payment = new CardPayment(cardId); + +// Good - factory +public final class PaymentFactory { + public static Payment create(PaymentType type, String ref) { + return switch (type) { + case CARD -> new CardPayment(ref); + case BANK -> new BankTransfer(ref); + }; + } +} +``` + +--- + +## Best Practices + +### Prefer Immutability + +```java +// Bad - mutable data carrier +public class Money { + public BigDecimal amount; + public String currency; +} + +// Good - immutable record +public record Money(BigDecimal amount, String currency) {} +``` + +### Use Dependency Injection + +```java +// Bad - hard-coded dependency +public class ReportService { + private final EmailClient emailClient = new EmailClient(); +} + +// Good - constructor injection +public class ReportService { + private final EmailClient emailClient; + + public ReportService(EmailClient emailClient) { + this.emailClient = emailClient; + } +} +``` + +### Keep Methods Small + +```java +// Bad - large method doing too much +public void processOrder(Order order) { + validate(order); + save(order); + notifyUser(order); + updateAnalytics(order); +} + +// Good - delegate to focused methods +public void processOrder(Order order) { + validate(order); + persist(order); + notifyUser(order); + track(order); +} +``` + +### Avoid Nulls When Possible + +```java +// Bad - returning null +public User findUser(String id) { + return userRepository.findById(id).orElse(null); +} + +// Good - Optional return +public Optional findUser(String id) { + return userRepository.findById(id); +} +``` + +--- + +## Java Simplification Checklist + +- [ ] Following Java naming conventions and formatting +- [ ] Using records for simple data carriers +- [ ] Switch expressions instead of verbose switch statements +- [ ] Streams for transformations; loops for simple checks +- [ ] Optional used for return types, not fields +- [ ] Prefer interfaces for collections +- [ ] Use List.copyOf/Set.copyOf/Map.copyOf for immutability +- [ ] Specific exceptions and try-with-resources +- [ ] No raw types; generics are bounded when appropriate +- [ ] Prefer builders for complex construction +- [ ] Avoid null returns; use Optional +- [ ] Methods are small and focused + +--- + +## Additional Resources + +- Java 17 Documentation: https://docs.oracle.com/en/java/javase/17/ +- Java Language Updates: https://openjdk.org/projects/jdk/ +- Effective Java (3rd Edition): https://www.oreilly.com/library/view/effective-java/9780134686097/ +- Java Streams Guide: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/stream/package-summary.html +- Java Optional: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Optional.html + +**Java Version Recommendation**: Use Java 17+ for best +language features and performance. diff --git a/code-simplifier/steering/js.md b/code-simplifier/steering/js.md new file mode 100644 index 0000000..7375eaf --- /dev/null +++ b/code-simplifier/steering/js.md @@ -0,0 +1,594 @@ +# JavaScript Code Simplification Guide + +This guide covers JavaScript-focused simplification +patterns and best practices for modern development. +For TypeScript-specific guidance, load `typescript.md`. + +## Table of Contents + +1. [Modern JavaScript Features](#modern-javascript-features) +2. [When to Load TypeScript Guidance](#when-to-load-typescript-guidance) +3. [Async/Await Patterns](#asyncawait-patterns) +4. [Array and Object Operations](#array-and-object-operations) +5. [Function Patterns](#function-patterns) +6. [Error Handling](#error-handling) +7. [React Patterns](#react-patterns) + +--- + +## Modern JavaScript Features + +### Destructuring + +```javascript +// Bad - accessing properties repeatedly +function displayUser(user) { + console.log(user.name); + console.log(user.email); + console.log(user.age); +} + +// Good - destructuring +function displayUser({ name, email, age }) { + console.log(name); + console.log(email); + console.log(age); +} + +// Array destructuring +const [first, second, ...rest] = array; + +// Nested destructuring +const { + user: { name, email } +} = response; + +// Default values +const { name = "Anonymous", age = 0 } = user; +``` + +### Spread and Rest Operators + +```javascript +// Object spreading +const defaults = { theme: "light", language: "en" }; +const userPrefs = { language: "fr" }; +const config = { ...defaults, ...userPrefs }; + +// Array spreading +const combined = [...array1, ...array2]; + +// Rest parameters +function sum(...numbers) { + return numbers.reduce((total, num) => total + num, 0); +} + +// Object rest +const { id, ...userData } = user; +``` + +### Template Literals + +```javascript +// Bad - string concatenation +const message = + "Hello, " + + user.name + + "! You have " + + count + + " messages."; + +// Good - template literals +const message = `Hello, ${user.name}! You have ${count} messages.`; + +// Multi-line strings +const html = ` +
+

${user.name}

+

${user.email}

+
+`; +``` + +### Optional Chaining and Nullish Coalescing + +```javascript +// Bad - nested null checks +const city = user && user.address && user.address.city; + +// Good - optional chaining +const city = user?.address?.city; + +// Nullish coalescing +const displayName = user.name ?? "Anonymous"; + +// Combined +const city = user?.address?.city ?? "Unknown"; +``` + +--- + +## When to Load TypeScript Guidance + +Load `typescript.md` instead of relying on this file when: + +- The file being simplified is `.ts` or `.tsx` +- Type design materially affects the refactor +- React props, state, or hooks depend on TypeScript types +- Utility types, generics, discriminated unions, or strict null handling matter + +--- + +## Async/Await Patterns + +### Basic Async/Await + +```javascript +// Bad - promise chains +function getUser(id) { + return fetch(`/api/users/${id}`) + .then((response) => response.json()) + .then((user) => { + return fetch(`/api/orders/${user.id}`) + .then((response) => response.json()) + .then((orders) => { + user.orders = orders; + return user; + }); + }); +} + +// Good - async/await +async function getUser(id) { + const response = await fetch(`/api/users/${id}`); + const user = await response.json(); + + const ordersResponse = await fetch( + `/api/orders/${user.id}` + ); + const orders = await ordersResponse.json(); + + return { ...user, orders }; +} +``` + +### Parallel Async Operations + +```javascript +// Bad - sequential awaits +async function getData() { + const users = await fetchUsers(); + const products = await fetchProducts(); + const orders = await fetchOrders(); + return { users, products, orders }; +} + +// Good - parallel execution +async function getData() { + const [users, products, orders] = await Promise.all([ + fetchUsers(), + fetchProducts(), + fetchOrders() + ]); + return { users, products, orders }; +} + +// Promise.allSettled for handling failures +async function getDataSafely() { + const results = await Promise.allSettled([ + fetchUsers(), + fetchProducts(), + fetchOrders() + ]); + + return results.map((result) => + result.status === "fulfilled" ? result.value : null + ); +} +``` + +### Error Handling with Async/Await + +```javascript +// Proper error handling +async function fetchUserData(id) { + try { + const response = await fetch(`/api/users/${id}`); + + if (!response.ok) { + throw new Error( + `HTTP error! status: ${response.status}` + ); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error("Failed to fetch user:", error); + throw error; // Re-throw or handle appropriately + } +} + +// Wrapper for cleaner error handling +async function safeAsync(fn) { + try { + const data = await fn(); + return [null, data]; + } catch (error) { + return [error, null]; + } +} + +// Usage +const [error, user] = await safeAsync(() => fetchUser(id)); +if (error) { + console.error(error); +} else { + console.log(user); +} +``` + +--- + +## Array and Object Operations + +### Array Methods + +```javascript +// Filter +const activeUsers = users.filter((user) => user.isActive); + +// Map +const userNames = users.map((user) => user.name); + +// Reduce +const totalPrice = items.reduce( + (sum, item) => sum + item.price, + 0 +); + +// Find +const user = users.find((user) => user.id === targetId); + +// Some and Every +const hasAdmin = users.some( + (user) => user.role === "admin" +); +const allActive = users.every((user) => user.isActive); + +// Chaining +const result = users + .filter((user) => user.isActive) + .map((user) => ({ id: user.id, name: user.name })) + .sort((a, b) => a.name.localeCompare(b.name)); +``` + +### Object Operations + +```javascript +// Object.entries for iteration +Object.entries(user).forEach(([key, value]) => { + console.log(`${key}: ${value}`); +}); + +// Object.fromEntries to create object +const userMap = Object.fromEntries( + users.map((user) => [user.id, user]) +); + +// Object.keys and Object.values +const keys = Object.keys(user); +const values = Object.values(user); + +// Computed property names +const dynamicKey = "email"; +const user = { + name: "John", + [dynamicKey]: "john@example.com" +}; +``` + +--- + +## Function Patterns + +### Arrow Functions + +```javascript +// Use arrow functions for callbacks +const doubled = numbers.map((n) => n * 2); + +// Implicit return for single expressions +const getFullName = (user) => + `${user.firstName} ${user.lastName}`; + +// Explicit return for multiple statements +const processUser = (user) => { + const fullName = `${user.firstName} ${user.lastName}`; + const age = calculateAge(user.birthDate); + return { fullName, age }; +}; + +// When NOT to use arrow functions +class User { + constructor(name) { + this.name = name; + } + + // Bad - arrow function loses 'this' context + greet = () => { + console.log(`Hello, ${this.name}`); + }; + + // Good - regular method + greet() { + console.log(`Hello, ${this.name}`); + } +} +``` + +### Function Composition + +```javascript +// Compose functions for reusability +const pipe = + (...fns) => + (x) => + fns.reduce((v, f) => f(v), x); + +const addTax = (price) => price * 1.2; +const applyDiscount = (price) => price * 0.9; +const formatPrice = (price) => `$${price.toFixed(2)}`; + +const calculateFinalPrice = pipe( + addTax, + applyDiscount, + formatPrice +); + +const finalPrice = calculateFinalPrice(100); // "$108.00" +``` + +### Currying + +```javascript +// Currying for partial application +const multiply = (a) => (b) => a * b; +const double = multiply(2); +const triple = multiply(3); + +console.log(double(5)); // 10 +console.log(triple(5)); // 15 + +// Practical example +const createLogger = (prefix) => (message) => { + console.log(`[${prefix}] ${message}`); +}; + +const errorLogger = createLogger("ERROR"); +const infoLogger = createLogger("INFO"); + +errorLogger("Something went wrong"); // [ERROR] Something went wrong +infoLogger("Process completed"); // [INFO] Process completed +``` + +--- + +## Error Handling + +### Custom Error Classes + +```javascript +// Create custom error classes +class ValidationError extends Error { + constructor(message, field) { + super(message); + this.name = "ValidationError"; + this.field = field; + } +} + +class NotFoundError extends Error { + constructor(message, resourceType, resourceId) { + super(message); + this.name = "NotFoundError"; + this.resourceType = resourceType; + this.resourceId = resourceId; + } +} + +// Usage +function validateUser(user) { + if (!user.email) { + throw new ValidationError( + "Email is required", + "email" + ); + } +} + +function findUser(id) { + const user = users.find((u) => u.id === id); + if (!user) { + throw new NotFoundError( + "User not found", + "User", + id + ); + } + return user; +} +``` + +### Error Boundaries (React) + +```javascript +// Error boundary component +class ErrorBoundary extends React.Component { + state = { hasError: false, error: null }; + + static getDerivedStateFromError(error) { + return { hasError: true, error }; + } + + componentDidCatch(error, errorInfo) { + console.error('Error caught by boundary:', error, errorInfo); + } + + render() { + if (this.state.hasError) { + return
Something went wrong: {this.state.error?.message}
; + } + + return this.props.children; + } +} +``` + +--- + +## React Patterns + +### Functional Components with Hooks + +```javascript +// Bad - class component +class UserProfile extends React.Component { + state = { user: null, loading: true }; + + componentDidMount() { + this.fetchUser(); + } + + fetchUser = async () => { + const user = await api.getUser(this.props.userId); + this.setState({ user, loading: false }); + }; + + render() { + if (this.state.loading) return
Loading...
; + return
{this.state.user.name}
; + } +} + +// Good - functional component with hooks +function UserProfile({ userId }) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchUser() { + const userData = await api.getUser(userId); + setUser(userData); + setLoading(false); + } + fetchUser(); + }, [userId]); + + if (loading) return
Loading...
; + return
{user?.name}
; +} +``` + +### Custom Hooks + +```javascript +// Extract reusable logic into custom hooks +function useUser(userId) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + async function fetchUser() { + try { + setLoading(true); + const userData = await api.getUser(userId); + setUser(userData); + } catch (err) { + setError(err); + } finally { + setLoading(false); + } + } + fetchUser(); + }, [userId]); + + return { user, loading, error }; +} + +// Usage +function UserProfile({ userId }) { + const { user, loading, error } = useUser(userId); + + if (loading) return
Loading...
; + if (error) return
Error: {error.message}
; + return
{user?.name}
; +} +``` + +### Component Composition + +```javascript +// Bad - prop drilling +function App() { + const [user, setUser] = useState(null); + return ; +} + +function Dashboard({ user, setUser }) { + return ; +} + +function Sidebar({ user, setUser }) { + return ; +} + +// Good - Context API +const UserContext = createContext(null); + +function App() { + const [user, setUser] = useState(null); + + return ( + + + + ); +} + +function UserMenu() { + const context = useContext(UserContext); + if (!context) throw new Error('UserMenu must be used within UserContext'); + + const { user, setUser } = context; + return
{user?.name}
; +} +``` + +--- + +## JavaScript Simplification Checklist + +- [ ] Using modern ES6+ features (destructuring, spread, etc.) +- [ ] Async/await instead of promise chains +- [ ] Arrow functions for callbacks +- [ ] Array methods (map, filter, reduce) instead of loops +- [ ] Optional chaining and nullish coalescing +- [ ] Custom error classes for specific errors +- [ ] Functional components with hooks (React) +- [ ] Custom hooks for reusable logic (React) +- [ ] Proper error handling with try/catch +- [ ] No nested ternary operators +- [ ] Template literals for string interpolation + +--- + +## Additional Resources + +- MDN Web Docs: + https://developer.mozilla.org/en-US/docs/Web/JavaScript +- React Documentation: https://react.dev/ +- JavaScript.info: https://javascript.info/ + +**Recommended Versions**: Node.js 18+, React 18+ diff --git a/code-simplifier/steering/kotlin.md b/code-simplifier/steering/kotlin.md new file mode 100644 index 0000000..e581e9e --- /dev/null +++ b/code-simplifier/steering/kotlin.md @@ -0,0 +1,443 @@ +# Kotlin Code Simplification Guide + +This guide covers Kotlin-specific simplification patterns and +best practices for writing idiomatic Kotlin code. + +## Table of Contents + +1. [Kotlin Style and Conventions](#kotlin-style-and-conventions) +2. [Null Safety](#null-safety) +3. [Data Classes](#data-classes) +4. [Extension Functions](#extension-functions) +5. [Coroutines](#coroutines) +6. [Scope Functions](#scope-functions) +7. [Collections](#collections) +8. [Sealed Classes](#sealed-classes) +9. [Interop with Java](#interop-with-java) + +--- + +## Kotlin Style and Conventions + +### Naming Conventions + +```kotlin +// Variables and functions - camelCase +val userName = "Sam" +fun calculateTotal(items: List): Double { + // implementation + return 0.0 +} + +// Types and interfaces - PascalCase +data class UserProfile(val id: String, val email: String) +interface Drawable { + fun draw() +} + +// Constants - UPPER_SNAKE_CASE (use const val or top-level val) +const val MAX_RETRIES = 3 +val DEFAULT_TIMEOUT_SECONDS = 30 + +// Packages - lowercase, no underscores +package com.example.billing +``` + +### Prefer Expression Style + +```kotlin +// Bad - verbose block and temporary variable +fun isAdult(age: Int): Boolean { + val result = if (age >= 18) true else false + return result +} + +// Good - expression body +fun isAdult(age: Int): Boolean = age >= 18 +``` + +--- + +## Null Safety + +### Avoid Nullable Types When Not Needed + +```kotlin +// Bad - nullable without reason +data class User(val id: String, val email: String?) + +fun sendReceipt(user: User) { + if (user.email != null) { + sendEmail(user.email) + } +} + +// Good - make nullability explicit and constrained +data class User(val id: String, val email: String) + +fun sendReceipt(user: User) = sendEmail(user.email) +``` + +### Prefer Safe Calls and Elvis + +```kotlin +// Bad - non-null assertion can crash +val length = user.nickname!!.length + +// Good - safe call with default +val length = user.nickname?.length ?: 0 +``` + +### Use let for Scoped Nullable Handling + +```kotlin +// Bad - nested ifs +if (user != null) { + if (user.address != null) { + println(user.address.city) + } +} + +// Good - safe calls and let +user?.address?.let { address -> + println(address.city) +} +``` + +--- + +## Data Classes + +### Prefer Data Classes for Value Objects + +```kotlin +// Bad - manual boilerplate +class Money(val amount: Long, val currency: String) { + override fun equals(other: Any?): Boolean { + if (other !is Money) return false + return amount == other.amount && currency == other.currency + } + + override fun hashCode(): Int = 31 * amount.hashCode() + currency.hashCode() + + override fun toString(): String = "Money(amount=$amount, currency=$currency)" +} + +// Good - data class +data class Money(val amount: Long, val currency: String) +``` + +### Use copy for Immutable Updates + +```kotlin +data class User(val id: String, val name: String, val active: Boolean) + +// Bad - mutable updates +val user = User("u1", "Sam", true) +val updated = User(user.id, "Samantha", user.active) + +// Good - copy +val updated = user.copy(name = "Samantha") +``` + +--- + +## Extension Functions + +### Encapsulate Reusable Logic + +```kotlin +// Bad - utility object and noisy call site +object StringUtils { + fun String.toSlug(): String = lowercase().replace(" ", "-") +} + +val slug = StringUtils.run { "Hello World".toSlug() } + +// Good - extension function at top-level +fun String.toSlug(): String = lowercase().replace(" ", "-") + +val slug = "Hello World".toSlug() +``` + +### Avoid Overusing Extensions + +```kotlin +// Bad - hides dependencies and side effects +fun HttpClient.postJson(url: String, body: String): HttpResponse { + return this.post(url, body) +} + +// Good - keep behavior explicit when dependencies are involved +class ApiClient(private val http: HttpClient) { + fun postJson(url: String, body: String): HttpResponse { + return http.post(url, body) + } +} +``` + +--- + +## Coroutines + +### Prefer Structured Concurrency + +```kotlin +// Bad - GlobalScope leaks lifecycle +fun loadUserData() { + GlobalScope.launch { + val user = api.fetchUser() + saveUser(user) + } +} + +// Good - tie to a scope +class UserRepository(private val scope: CoroutineScope) { + fun loadUserData() = scope.launch { + val user = api.fetchUser() + saveUser(user) + } +} +``` + +### Use suspend and withContext + +```kotlin +// Bad - blocking call on main thread +suspend fun readFile(path: String): String { + return File(path).readText() +} + +// Good - shift to IO dispatcher +suspend fun readFile(path: String): String = withContext(Dispatchers.IO) { + File(path).readText() +} +``` + +### Use async for Parallel Work + +```kotlin +// Bad - sequential calls +suspend fun loadDashboard(): Dashboard { + val user = api.fetchUser() + val stats = api.fetchStats() + return Dashboard(user, stats) +} + +// Good - parallel with async +suspend fun loadDashboard(): Dashboard = coroutineScope { + val user = async { api.fetchUser() } + val stats = async { api.fetchStats() } + Dashboard(user.await(), stats.await()) +} +``` + +--- + +## Scope Functions + +### Choose the Right Scope Function + +```kotlin +data class Config(var host: String, var port: Int) + +// Bad - confusing nested scope usage +val config = Config("localhost", 8080).also { + it.host = "api.local" + it.port = 9090 +}.run { + "http://$host:$port" +} + +// Good - use apply for configuration and let for transformation +val config = Config("localhost", 8080).apply { + host = "api.local" + port = 9090 +} +val baseUrl = config.let { "http://${it.host}:${it.port}" } +``` + +### Avoid Overusing It + +```kotlin +// Bad - ambiguous it chain +user?.let { it.profile }?.let { it.preferences }?.let { it.theme } + +// Good - name values for clarity +val theme = user?.profile?.preferences?.theme +``` + +--- + +## Collections + +### Prefer Immutable Collections + +```kotlin +// Bad - mutable lists by default +val users = mutableListOf("a", "b", "c") +users.add("d") + +// Good - immutable list and create new list when needed +val users = listOf("a", "b", "c") +val updated = users + "d" +``` + +### Use Standard Library Functions + +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) + +// Bad - manual loop +val evenSquares = mutableListOf() +for (n in numbers) { + if (n % 2 == 0) { + evenSquares.add(n * n) + } +} + +// Good - filter/map +val evenSquares = numbers.filter { it % 2 == 0 }.map { it * it } +``` + +### Avoid Temporary Collections + +```kotlin +// Bad - intermediate list +val total = numbers.map { it * 2 }.filter { it > 5 }.sum() + +// Good - use sequence for large data +val total = numbers.asSequence() + .map { it * 2 } + .filter { it > 5 } + .sum() +``` + +--- + +## Sealed Classes + +### Model Exhaustive State + +```kotlin +// Bad - enum with extra data via nullable fields +data class Result(val status: String, val error: String?, val data: String?) + +// Good - sealed hierarchy +sealed class Result { + data class Success(val data: String) : Result() + data class Error(val message: String) : Result() + object Loading : Result() +} + +fun render(result: Result): String = when (result) { + is Result.Success -> "Data: ${result.data}" + is Result.Error -> "Error: ${result.message}" + Result.Loading -> "Loading" +} +``` + +### Use when Without else + +```kotlin +// Bad - else hides missing cases +fun label(result: Result): String = when (result) { + is Result.Success -> "OK" + else -> "Other" +} + +// Good - exhaustive when +fun label(result: Result): String = when (result) { + is Result.Success -> "OK" + is Result.Error -> "Error" + Result.Loading -> "Loading" +} +``` + +--- + +## Interop with Java + +### Use @JvmStatic and @JvmOverloads + +```kotlin +// Bad - Java callers get awkward usage +class Dates { + companion object { + fun nowUtc(): Instant = Instant.now() + } + + fun format(date: Instant, pattern: String = "yyyy-MM-dd"): String { + return DateTimeFormatter.ofPattern(pattern) + .withZone(ZoneOffset.UTC) + .format(date) + } +} + +// Good - Java-friendly APIs +class Dates { + companion object { + @JvmStatic fun nowUtc(): Instant = Instant.now() + } + + @JvmOverloads + fun format(date: Instant, pattern: String = "yyyy-MM-dd"): String { + return DateTimeFormatter.ofPattern(pattern) + .withZone(ZoneOffset.UTC) + .format(date) + } +} +``` + +### Avoid Platform Types with Explicit Nullability + +```kotlin +// Bad - platform types from Java are nullable at runtime +val name = javaUser.name +val length = name.length + +// Good - use safe call or requireNotNull +val name = javaUser.name +val length = name?.length ?: 0 + +val strictName = requireNotNull(javaUser.name) { "name is required" } +``` + +### Prefer Kotlin Types and Builders + +```kotlin +// Bad - Java collections in public API +fun loadUsers(): java.util.List = java.util.ArrayList() + +// Good - Kotlin collections +fun loadUsers(): List = emptyList() +``` + +--- + +## Kotlin Simplification Checklist + +- [ ] Following Kotlin naming conventions (camelCase, + PascalCase) +- [ ] Nullability is explicit and minimized +- [ ] Using data classes for value objects +- [ ] Extension functions are small and focused +- [ ] Structured concurrency for coroutines +- [ ] Scope functions chosen intentionally +- [ ] Prefer immutable collections +- [ ] Sealed classes with exhaustive when +- [ ] Java interop annotations as needed +- [ ] No unsafe !! in core flows + +--- + +## Additional Resources + +- Kotlin Coding Conventions: https://kotlinlang.org/docs/coding-conventions.html +- Kotlin Coroutines Guide: https://kotlinlang.org/docs/coroutines-guide.html +- Effective Kotlin: https://kt.academy/book/effectivekotlin +- Kotlin Standard Library: https://kotlinlang.org/api/latest/jvm/stdlib/ +- Interop with Java: https://kotlinlang.org/docs/java-interop.html + +**Kotlin Version Recommendation**: Use the latest stable +version for best features and tooling. diff --git a/code-simplifier/steering/php-laravel.md b/code-simplifier/steering/php-laravel.md new file mode 100644 index 0000000..cbf9460 --- /dev/null +++ b/code-simplifier/steering/php-laravel.md @@ -0,0 +1,638 @@ +# Laravel Code Simplification Guide + +This guide covers Laravel-specific patterns, conventions, +and best practices for writing elegant, maintainable code. + +## Table of Contents + +1. [Laravel Conventions](#laravel-conventions) +2. [Eloquent Best Practices](#eloquent-best-practices) +3. [Controller Patterns](#controller-patterns) +4. [Service Layer](#service-layer) +5. [Validation](#validation) +6. [Query Optimization](#query-optimization) +7. [Collections](#collections) +8. [Routing](#routing) + +--- + +## Laravel Conventions + +### Directory Structure + +Follow Laravel's standard directory structure: + +``` +app/ +├── Http/ +│ ├── Controllers/ +│ ├── Middleware/ +│ ├── Requests/ +│ └── Resources/ +├── Models/ +├── Services/ +├── Repositories/ +├── Exceptions/ +└── Providers/ +``` + +### Naming Conventions + +```php +// Controllers - singular, Controller suffix +UserController, OrderController + +// Models - singular +User, Order, Product + +// Migrations - descriptive +create_users_table, add_status_to_orders_table + +// Routes - plural for resources +Route::resource('users', UserController::class); +Route::resource('orders', OrderController::class); + +// Database tables - plural, snake_case +users, orders, order_items + +// Pivot tables - alphabetical, singular +order_product, role_user +``` + +--- + +## Eloquent Best Practices + +### Model Organization + +```php + 'decimal:2', + 'completed_at' => 'datetime', + ]; + + // 3. Relationships + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function items(): HasMany + { + return $this->hasMany(OrderItem::class); + } + + // 4. Scopes + public function scopeCompleted($query) + { + return $query->where('status', self::STATUS_COMPLETED); + } + + public function scopeRecent($query) + { + return $query->where('created_at', '>=', now()->subDays(30)); + } + + // 5. Accessors & Mutators + public function getTotalFormattedAttribute(): string + { + return '$' . number_format($this->total, 2); + } + + // 6. Business Logic Methods + public function markAsCompleted(): void + { + $this->update([ + 'status' => self::STATUS_COMPLETED, + 'completed_at' => now(), + ]); + } + + public function isCompleted(): bool + { + return $this->status === self::STATUS_COMPLETED; + } +} +``` + +### Relationship Type Hints + +```php +// Always type hint relationships +public function posts(): HasMany +{ + return $this->hasMany(Post::class); +} + +public function author(): BelongsTo +{ + return $this->belongsTo(User::class, 'author_id'); +} + +public function tags(): BelongsToMany +{ + return $this->belongsToMany(Tag::class) + ->withTimestamps() + ->withPivot('order'); +} +``` + +### Query Scopes + +```php +// Bad - repeating query logic +$activeUsers = User::where('is_active', true)->get(); +$activeAdmins = User::where('is_active', true)->where('role', 'admin')->get(); + +// Good - reusable scopes +class User extends Model +{ + public function scopeActive($query) + { + return $query->where('is_active', true); + } + + public function scopeAdmins($query) + { + return $query->where('role', 'admin'); + } +} + +$activeUsers = User::active()->get(); +$activeAdmins = User::active()->admins()->get(); +``` + +--- + +## Controller Patterns + +### Thin Controllers + +```php +// Bad - fat controller +class OrderController extends Controller +{ + public function store(Request $request) + { + // Validation + $validated = $request->validate([ + 'items' => 'required|array', + 'items.*.product_id' => 'required|exists:products,id', + 'items.*.quantity' => 'required|integer|min:1', + ]); + + // Business logic + $total = 0; + foreach ($validated['items'] as $item) { + $product = Product::find($item['product_id']); + $total += $product->price * $item['quantity']; + } + + // Create order + $order = Order::create([ + 'user_id' => auth()->id(), + 'total' => $total, + 'status' => 'pending', + ]); + + // Create order items + foreach ($validated['items'] as $item) { + $order->items()->create($item); + } + + // Send email + Mail::to($request->user())->send(new OrderConfirmation($order)); + + return response()->json($order, 201); + } +} + +// Good - thin controller with Form Request and Service +class OrderController extends Controller +{ + public function __construct( + private OrderService $orderService + ) { + } + + public function store(StoreOrderRequest $request) + { + $order = $this->orderService->createOrder( + user: $request->user(), + items: $request->validated('items') + ); + + return new OrderResource($order); + } +} +``` + +### Form Requests + +```php +// Extract validation to Form Request classes +class StoreOrderRequest extends FormRequest +{ + public function authorize(): bool + { + return true; + } + + public function rules(): array + { + return [ + 'items' => ['required', 'array', 'min:1'], + 'items.*.product_id' => ['required', 'exists:products,id'], + 'items.*.quantity' => ['required', 'integer', 'min:1', 'max:100'], + ]; + } + + public function messages(): array + { + return [ + 'items.*.product_id.exists' => 'One or more products do not exist.', + 'items.*.quantity.max' => 'Maximum quantity per item is 100.', + ]; + } +} +``` + +### Resource Classes + +```php +// Use API Resources for consistent response formatting +class OrderResource extends JsonResource +{ + public function toArray($request): array + { + return [ + 'id' => $this->id, + 'total' => $this->total_formatted, + 'status' => $this->status, + 'items' => OrderItemResource::collection($this->whenLoaded('items')), + 'user' => new UserResource($this->whenLoaded('user')), + 'created_at' => $this->created_at->toISOString(), + ]; + } +} +``` + +--- + +## Service Layer + +### Service Classes + +```php +// Create service classes for complex business logic +class OrderService +{ + public function __construct( + private OrderRepository $orderRepository, + private ProductRepository $productRepository, + private NotificationService $notificationService + ) { + } + + public function createOrder(User $user, array $items): Order + { + $total = $this->calculateTotal($items); + + $order = $this->orderRepository->create([ + 'user_id' => $user->id, + 'total' => $total, + 'status' => Order::STATUS_PENDING, + ]); + + $this->createOrderItems($order, $items); + $this->notificationService->sendOrderConfirmation($order); + + return $order->load('items'); + } + + private function calculateTotal(array $items): float + { + return collect($items)->sum(function ($item) { + $product = $this->productRepository->find($item['product_id']); + return $product->price * $item['quantity']; + }); + } + + private function createOrderItems(Order $order, array $items): void + { + foreach ($items as $item) { + $order->items()->create($item); + } + } +} +``` + +### Repository Pattern (Optional) + +```php +// Use repositories for complex queries or when you need to swap implementations +class OrderRepository +{ + public function findWithItems(int $id): ?Order + { + return Order::with('items.product')->find($id); + } + + public function getRecentOrders(User $user, int $days = 30): Collection + { + return Order::where('user_id', $user->id) + ->where('created_at', '>=', now()->subDays($days)) + ->with('items') + ->latest() + ->get(); + } + + public function create(array $data): Order + { + return Order::create($data); + } +} +``` + +--- + +## Validation + +### Custom Validation Rules + +```php +// Create custom rule classes for complex validation +class ValidCouponCode implements Rule +{ + public function passes($attribute, $value): bool + { + return Coupon::where('code', $value) + ->where('expires_at', '>', now()) + ->where('is_active', true) + ->exists(); + } + + public function message(): string + { + return 'The coupon code is invalid or expired.'; + } +} + +// Usage in Form Request +public function rules(): array +{ + return [ + 'coupon_code' => ['nullable', 'string', new ValidCouponCode], + ]; +} +``` + +### Conditional Validation + +```php +// Use conditional validation rules +public function rules(): array +{ + return [ + 'payment_method' => ['required', 'in:card,paypal,bank'], + 'card_number' => [ + 'required_if:payment_method,card', + 'string', + 'size:16', + ], + 'paypal_email' => [ + 'required_if:payment_method,paypal', + 'email', + ], + ]; +} +``` + +--- + +## Query Optimization + +### Eager Loading + +```php +// Bad - N+1 query problem +$orders = Order::all(); +foreach ($orders as $order) { + echo $order->user->name; // Queries user for each order +} + +// Good - eager loading +$orders = Order::with('user')->get(); +foreach ($orders as $order) { + echo $order->user->name; // No additional queries +} + +// Nested eager loading +$orders = Order::with(['user', 'items.product'])->get(); + +// Conditional eager loading +$orders = Order::with(['user' => function ($query) { + $query->select('id', 'name', 'email'); +}])->get(); +``` + +### Query Optimization + +```php +// Bad - loading unnecessary data +$users = User::all(); + +// Good - select only needed columns +$users = User::select('id', 'name', 'email')->get(); + +// Use chunk for large datasets +User::chunk(100, function ($users) { + foreach ($users as $user) { + // Process user + } +}); + +// Use cursor for memory efficiency +foreach (User::cursor() as $user) { + // Process user with minimal memory usage +} +``` + +### Avoid Query in Loops + +```php +// Bad - query in loop +foreach ($orderIds as $orderId) { + $order = Order::find($orderId); + // process order +} + +// Good - single query +$orders = Order::whereIn('id', $orderIds)->get(); +foreach ($orders as $order) { + // process order +} +``` + +--- + +## Collections + +### Collection Methods + +```php +// Use Laravel collections for data manipulation + +// Bad - manual loops +$activeUsers = []; +foreach ($users as $user) { + if ($user->is_active) { + $activeUsers[] = $user; + } +} + +// Good - collection methods +$activeUsers = $users->filter(fn($user) => $user->is_active); + +// Chaining collection methods +$result = $orders + ->filter(fn($order) => $order->isCompleted()) + ->map(fn($order) => [ + 'id' => $order->id, + 'total' => $order->total, + ]) + ->sortByDesc('total') + ->take(10); + +// Grouping +$ordersByStatus = $orders->groupBy('status'); + +// Plucking +$userIds = $orders->pluck('user_id')->unique(); + +// Sum and average +$totalRevenue = $orders->sum('total'); +$averageOrderValue = $orders->avg('total'); +``` + +### Higher Order Messages + +```php +// Use higher order messages for cleaner code +$names = $users->map->name; +$activeUsers = $users->filter->isActive(); +$totalSpent = $users->sum->total_spent; +``` + +--- + +## Routing + +### Route Model Binding + +```php +// Bad - manual model lookup +Route::get('/users/{id}', function ($id) { + $user = User::findOrFail($id); + return view('users.show', compact('user')); +}); + +// Good - route model binding +Route::get('/users/{user}', function (User $user) { + return view('users.show', compact('user')); +}); + +// Custom key for route model binding +public function getRouteKeyName(): string +{ + return 'slug'; +} + +// Route: /posts/{post:slug} +Route::get('/posts/{post:slug}', function (Post $post) { + return view('posts.show', compact('post')); +}); +``` + +### Resource Controllers + +```php +// Use resource controllers for RESTful routes +Route::resource('orders', OrderController::class); + +// API resource routes (no create/edit) +Route::apiResource('orders', OrderController::class); + +// Limit resource routes +Route::resource('orders', OrderController::class)->only(['index', 'show']); +Route::resource('orders', OrderController::class)->except(['destroy']); +``` + +### Route Groups + +```php +// Group related routes +Route::prefix('admin') + ->middleware(['auth', 'admin']) + ->name('admin.') + ->group(function () { + Route::get('/dashboard', [AdminController::class, 'dashboard'])->name('dashboard'); + Route::resource('users', AdminUserController::class); + }); +``` + +--- + +## Laravel Simplification Checklist + +- [ ] Controllers are thin (business logic in services) +- [ ] Using Form Requests for validation +- [ ] Using API Resources for response formatting +- [ ] Relationships have return type hints +- [ ] Using query scopes for reusable queries +- [ ] Eager loading to avoid N+1 queries +- [ ] Using collections instead of manual loops +- [ ] Route model binding where applicable +- [ ] Service classes for complex business logic +- [ ] Following Laravel naming conventions +- [ ] Using enums for status/type constants (PHP 8.1+) +- [ ] Proper use of middleware +- [ ] Database transactions for multi-step operations + +--- + +## Additional Resources + +- Laravel Documentation: https://laravel.com/docs +- Laravel Best Practices: + https://github.com/alexeymezenin/laravel-best-practices +- Laracasts: https://laracasts.com + +**Laravel Version Recommendation**: Use Laravel 10+ with PHP +8.1+ for best features and support. diff --git a/code-simplifier/steering/php-symfony.md b/code-simplifier/steering/php-symfony.md new file mode 100644 index 0000000..21fdf90 --- /dev/null +++ b/code-simplifier/steering/php-symfony.md @@ -0,0 +1,753 @@ +# Symfony Code Simplification Guide + +This guide covers Symfony-specific patterns, conventions, +and best practices for writing clean, maintainable code. + +## Table of Contents + +1. [Symfony Conventions](#symfony-conventions) +2. [Controller Best Practices](#controller-best-practices) +3. [Service Configuration](#service-configuration) +4. [Doctrine ORM](#doctrine-orm) +5. [Form Handling](#form-handling) +6. [Validation](#validation) +7. [Event System](#event-system) + +--- + +## Symfony Conventions + +### Directory Structure + +Follow Symfony's standard directory structure: + +``` +src/ +├── Controller/ +├── Entity/ +├── Repository/ +├── Service/ +├── Form/ +├── EventListener/ +├── EventSubscriber/ +└── Kernel.php +``` + +### Naming Conventions + +```php +// Controllers - Controller suffix +UserController, OrderController + +// Entities - singular +User, Order, Product + +// Repositories - Repository suffix +UserRepository, OrderRepository + +// Services - descriptive names +EmailService, PaymentProcessor + +// Form Types - Type suffix +UserType, OrderType +``` + +--- + +## Controller Best Practices + +### Attribute-Based Routing (Symfony 6+) + +```php +orderService->getAllOrders(); + + return $this->render('order/index.html.twig', [ + 'orders' => $orders, + ]); + } + + #[Route('/{id}', name: 'show', methods: ['GET'])] + public function show(Order $order): Response + { + return $this->render('order/show.html.twig', [ + 'order' => $order, + ]); + } + + #[Route('/create', name: 'create', methods: ['POST'])] + public function create(Request $request): Response + { + $order = $this->orderService->createOrder( + $request->request->all() + ); + + return $this->json($order, Response::HTTP_CREATED); + } +} +``` + +### Thin Controllers + +```php +// Bad - fat controller +class OrderController extends AbstractController +{ + #[Route('/orders', methods: ['POST'])] + public function create(Request $request, EntityManagerInterface $em): Response + { + // Validation + $data = $request->request->all(); + if (empty($data['items'])) { + throw new BadRequestException('Items required'); + } + + // Business logic + $total = 0; + foreach ($data['items'] as $item) { + $product = $em->getRepository(Product::class)->find($item['product_id']); + $total += $product->getPrice() * $item['quantity']; + } + + // Create order + $order = new Order(); + $order->setUser($this->getUser()); + $order->setTotal($total); + $order->setStatus('pending'); + + $em->persist($order); + $em->flush(); + + return $this->json($order); + } +} + +// Good - thin controller with service +class OrderController extends AbstractController +{ + public function __construct( + private OrderService $orderService + ) { + } + + #[Route('/orders', methods: ['POST'])] + public function create(Request $request): Response + { + $order = $this->orderService->createOrder( + user: $this->getUser(), + items: $request->request->all('items') + ); + + return $this->json($order, Response::HTTP_CREATED); + } +} +``` + +### ParamConverter for Automatic Entity Loading + +```php +// Automatic entity loading from route parameters +#[Route('/orders/{id}', name: 'order_show')] +public function show(Order $order): Response +{ + // $order is automatically loaded by ID + return $this->render('order/show.html.twig', [ + 'order' => $order, + ]); +} + +// Custom field for loading +#[Route('/orders/{slug}', name: 'order_show_by_slug')] +public function showBySlug( + #[MapEntity(mapping: ['slug' => 'slug'])] + Order $order +): Response { + return $this->render('order/show.html.twig', [ + 'order' => $order, + ]); +} +``` + +--- + +## Service Configuration + +### Autowiring and Autoconfiguration + +```yaml +# config/services.yaml +services: + _defaults: + autowire: true + autoconfigure: true + + App\: + resource: "../src/" + exclude: + - "../src/DependencyInjection/" + - "../src/Entity/" + - "../src/Kernel.php" +``` + +### Service Constructor Injection + +```php +// Use constructor injection for dependencies +class OrderService +{ + public function __construct( + private EntityManagerInterface $entityManager, + private OrderRepository $orderRepository, + private EmailService $emailService, + private LoggerInterface $logger + ) { + } + + public function createOrder(User $user, array $items): Order + { + try { + $order = new Order(); + $order->setUser($user); + $order->setTotal($this->calculateTotal($items)); + + $this->entityManager->persist($order); + $this->entityManager->flush(); + + $this->emailService->sendOrderConfirmation($order); + + return $order; + } catch (\Exception $e) { + $this->logger->error('Order creation failed', [ + 'user_id' => $user->getId(), + 'error' => $e->getMessage(), + ]); + throw $e; + } + } + + private function calculateTotal(array $items): float + { + // Calculate total logic + } +} +``` + +### Service Aliases and Interfaces + +```php +// Define interface +interface PaymentProcessorInterface +{ + public function process(Order $order): bool; +} + +// Implementation +class StripePaymentProcessor implements PaymentProcessorInterface +{ + public function process(Order $order): bool + { + // Stripe payment logic + } +} + +// Configure in services.yaml +services: + App\Service\PaymentProcessorInterface: + alias: App\Service\StripePaymentProcessor + +// Use in other services +class OrderService +{ + public function __construct( + private PaymentProcessorInterface $paymentProcessor + ) { + } +} +``` + +--- + +## Doctrine ORM + +### Entity Best Practices + +```php +items = new ArrayCollection(); + $this->createdAt = new \DateTimeImmutable(); + } + + // Getters and setters with proper type hints + public function getId(): ?int + { + return $this->id; + } + + public function getUser(): User + { + return $this->user; + } + + public function setUser(User $user): self + { + $this->user = $user; + return $this; + } + + /** + * @return Collection + */ + public function getItems(): Collection + { + return $this->items; + } + + public function addItem(OrderItem $item): self + { + if (!$this->items->contains($item)) { + $this->items->add($item); + $item->setOrder($this); + } + + return $this; + } + + public function removeItem(OrderItem $item): self + { + if ($this->items->removeElement($item)) { + if ($item->getOrder() === $this) { + $item->setOrder(null); + } + } + + return $this; + } + + public function getTotal(): string + { + return $this->total; + } + + public function setTotal(string $total): self + { + $this->total = $total; + return $this; + } + + // Business logic methods + public function isPending(): bool + { + return $this->status === 'pending'; + } + + public function markAsCompleted(): void + { + $this->status = 'completed'; + } +} +``` + +### Repository Patterns + +```php +createQueryBuilder('o') + ->andWhere('o.user = :user') + ->andWhere('o.createdAt >= :date') + ->setParameter('user', $user) + ->setParameter('date', new \DateTimeImmutable("-{$days} days")) + ->orderBy('o.createdAt', 'DESC') + ->getQuery() + ->getResult(); + } + + public function findCompletedOrders(): array + { + return $this->createQueryBuilder('o') + ->andWhere('o.status = :status') + ->setParameter('status', 'completed') + ->getQuery() + ->getResult(); + } + + public function getTotalRevenue(): float + { + return (float) $this->createQueryBuilder('o') + ->select('SUM(o.total)') + ->andWhere('o.status = :status') + ->setParameter('status', 'completed') + ->getQuery() + ->getSingleScalarResult(); + } +} +``` + +### Query Optimization + +```php +// Bad - N+1 query problem +$orders = $orderRepository->findAll(); +foreach ($orders as $order) { + echo $order->getUser()->getName(); // Queries user for each order +} + +// Good - eager loading with joins +$orders = $orderRepository->createQueryBuilder('o') + ->leftJoin('o.user', 'u') + ->addSelect('u') + ->leftJoin('o.items', 'i') + ->addSelect('i') + ->getQuery() + ->getResult(); + +// Partial objects for better performance +$orders = $orderRepository->createQueryBuilder('o') + ->select('partial o.{id, total, status}') + ->leftJoin('o.user', 'u') + ->addSelect('partial u.{id, name, email}') + ->getQuery() + ->getResult(); +``` + +--- + +## Form Handling + +### Form Types + +```php +add('items', CollectionType::class, [ + 'entry_type' => OrderItemType::class, + 'allow_add' => true, + 'allow_delete' => true, + 'by_reference' => false, + ]) + ->add('submit', SubmitType::class, [ + 'label' => 'Create Order', + ]); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Order::class, + ]); + } +} +``` + +### Form Handling in Controllers + +```php +#[Route('/orders/new', name: 'order_new', methods: ['GET', 'POST'])] +public function new(Request $request, EntityManagerInterface $em): Response +{ + $order = new Order(); + $form = $this->createForm(OrderType::class, $order); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em->persist($order); + $em->flush(); + + $this->addFlash('success', 'Order created successfully'); + + return $this->redirectToRoute('order_show', ['id' => $order->getId()]); + } + + return $this->render('order/new.html.twig', [ + 'form' => $form, + ]); +} +``` + +--- + +## Validation + +### Validation Constraints + +```php +couponRepository->findActiveByCode($value); + + if (!$coupon) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $value) + ->addViolation(); + } + } +} + +// Use in entity +#[Assert\ValidCoupon] +private ?string $couponCode = null; +``` + +--- + +## Event System + +### Event Subscribers + +```php + 'onOrderCreated', + ]; + } + + public function onOrderCreated(OrderCreatedEvent $event): void + { + $order = $event->getOrder(); + + $this->emailService->sendOrderConfirmation($order); + } +} +``` + +### Dispatching Events + +```php +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +class OrderService +{ + public function __construct( + private EntityManagerInterface $entityManager, + private EventDispatcherInterface $eventDispatcher + ) { + } + + public function createOrder(User $user, array $items): Order + { + $order = new Order(); + $order->setUser($user); + // ... set other properties + + $this->entityManager->persist($order); + $this->entityManager->flush(); + + // Dispatch event + $this->eventDispatcher->dispatch( + new OrderCreatedEvent($order) + ); + + return $order; + } +} +``` + +--- + +## Symfony Simplification Checklist + +- [ ] Controllers are thin (business logic in services) +- [ ] Using attribute-based routing (Symfony 6+) +- [ ] Proper dependency injection via constructor +- [ ] Entity relationships properly configured +- [ ] Using QueryBuilder for complex queries +- [ ] Eager loading to avoid N+1 queries +- [ ] Form types for form handling +- [ ] Validation constraints on entities +- [ ] Event subscribers for cross-cutting concerns +- [ ] Repository methods for reusable queries +- [ ] Proper use of Doctrine cascade operations +- [ ] Type hints on all methods and properties + +--- + +## Additional Resources + +- Symfony Documentation: + https://symfony.com/doc/current/index.html +- Symfony Best Practices: + https://symfony.com/doc/current/best_practices.html +- Doctrine ORM Documentation: + https://www.doctrine-project.org/projects/doctrine-orm/en/current/index.html + +**Symfony Version Recommendation**: Use Symfony 6+ with PHP +8.1+ for best features and support. diff --git a/code-simplifier/steering/php.md b/code-simplifier/steering/php.md new file mode 100644 index 0000000..890f925 --- /dev/null +++ b/code-simplifier/steering/php.md @@ -0,0 +1,543 @@ +# PHP Code Simplification Guide + +This guide covers PHP-specific simplification patterns and +best practices following PSR standards. + +## Table of Contents + +1. [PHP Standards](#php-standards) +2. [Type Declarations](#type-declarations) +3. [Modern PHP Features](#modern-php-features) +4. [Error Handling](#error-handling) +5. [Array Operations](#array-operations) +6. [String Operations](#string-operations) +7. [Object-Oriented Patterns](#object-oriented-patterns) + +--- + +## PHP Standards + +### PSR-12 Compliance + +Follow PSR-12 coding style standard: + +```php +first(); +} +``` + +--- + +## Modern PHP Features + +### Constructor Property Promotion (PHP 8.0+) + +```php +// Bad - verbose +class UserService +{ + private UserRepository $repository; + private EmailService $emailService; + + public function __construct(UserRepository $repository, EmailService $emailService) + { + $this->repository = $repository; + $this->emailService = $emailService; + } +} + +// Good - promoted properties +class UserService +{ + public function __construct( + private UserRepository $repository, + private EmailService $emailService + ) { + } +} +``` + +### Match Expression (PHP 8.0+) + +```php +// Bad - switch statement +switch ($status) { + case 'pending': + $message = 'Order is pending'; + break; + case 'processing': + $message = 'Order is being processed'; + break; + case 'completed': + $message = 'Order is completed'; + break; + default: + $message = 'Unknown status'; +} + +// Good - match expression +$message = match ($status) { + 'pending' => 'Order is pending', + 'processing' => 'Order is being processed', + 'completed' => 'Order is completed', + default => 'Unknown status', +}; +``` + +### Nullsafe Operator (PHP 8.0+) + +```php +// Bad - nested null checks +$country = null; +if ($user !== null) { + if ($user->getAddress() !== null) { + $country = $user->getAddress()->getCountry(); + } +} + +// Good - nullsafe operator +$country = $user?->getAddress()?->getCountry(); +``` + +### Named Arguments (PHP 8.0+) + +```php +// Use named arguments for clarity with many parameters +public function createUser( + string $name, + string $email, + bool $isActive = true, + bool $isVerified = false, + ?string $phone = null +): User { + // ... +} + +// Bad - unclear what parameters mean +$user = $this->createUser('John', 'john@example.com', true, false, null); + +// Good - named arguments make intent clear +$user = $this->createUser( + name: 'John', + email: 'john@example.com', + isActive: true, + isVerified: false +); +``` + +--- + +## Error Handling + +### Use Exceptions, Not Error Codes + +```php +// Bad - error codes +public function processPayment($amount) +{ + if ($amount <= 0) { + return ['error' => true, 'code' => 'INVALID_AMOUNT']; + } + + if (!$this->hasBalance($amount)) { + return ['error' => true, 'code' => 'INSUFFICIENT_FUNDS']; + } + + return ['error' => false, 'data' => $this->charge($amount)]; +} + +// Good - exceptions +public function processPayment(float $amount): Payment +{ + if ($amount <= 0) { + throw new InvalidAmountException('Amount must be positive'); + } + + if (!$this->hasBalance($amount)) { + throw new InsufficientFundsException('Insufficient balance'); + } + + return $this->charge($amount); +} +``` + +### Custom Exception Classes + +```php +// Create specific exception classes +namespace App\Exceptions; + +class InvalidAmountException extends \InvalidArgumentException +{ +} + +class InsufficientFundsException extends \RuntimeException +{ +} + +class PaymentProcessingException extends \RuntimeException +{ +} +``` + +### Try-Catch Blocks + +```php +// Bad - catching generic Exception +try { + $payment = $this->processPayment($amount); +} catch (\Exception $e) { + return 'Error occurred'; +} + +// Good - catch specific exceptions +try { + $payment = $this->processPayment($amount); +} catch (InvalidAmountException $e) { + return 'Invalid payment amount'; +} catch (InsufficientFundsException $e) { + return 'Insufficient funds'; +} catch (PaymentProcessingException $e) { + Log::error('Payment processing failed', ['exception' => $e]); + return 'Payment processing failed'; +} +``` + +--- + +## Array Operations + +### Array Functions + +```php +// Use array functions instead of loops when appropriate + +// Bad - manual loop +$activeUsers = []; +foreach ($users as $user) { + if ($user->isActive()) { + $activeUsers[] = $user; + } +} + +// Good - array_filter +$activeUsers = array_filter($users, fn($user) => $user->isActive()); + +// Bad - manual loop for transformation +$userNames = []; +foreach ($users as $user) { + $userNames[] = $user->getName(); +} + +// Good - array_map +$userNames = array_map(fn($user) => $user->getName(), $users); +``` + +### Array Destructuring + +```php +// Use array destructuring for clarity +[$firstName, $lastName] = explode(' ', $fullName, 2); + +// With associative arrays +['name' => $name, 'email' => $email] = $userData; +``` + +### Spread Operator + +```php +// Use spread operator for array merging +$defaults = ['color' => 'blue', 'size' => 'medium']; +$custom = ['size' => 'large']; + +// Bad +$config = array_merge($defaults, $custom); + +// Good +$config = [...$defaults, ...$custom]; +``` + +--- + +## String Operations + +### String Interpolation + +```php +// Bad - concatenation +$message = 'Hello, ' . $user->getName() . '! Your balance is ' . $user->getBalance(); + +// Good - interpolation +$message = "Hello, {$user->getName()}! Your balance is {$user->getBalance()}"; + +// For simple variables +$message = "Hello, $name!"; +``` + +### Heredoc and Nowdoc + +```php +// Use heredoc for multi-line strings with interpolation +$email = << + +

Hello, {$user->getName()}

+

Your order #{$order->getId()} has been confirmed.

+ + +HTML; + +// Use nowdoc (single quotes) when no interpolation needed +$sql = <<<'SQL' +SELECT * FROM users +WHERE status = 'active' +AND created_at > DATE_SUB(NOW(), INTERVAL 30 DAY) +SQL; +``` + +--- + +## Object-Oriented Patterns + +### Readonly Properties (PHP 8.1+) + +```php +// Use readonly for immutable properties +class User +{ + public function __construct( + public readonly int $id, + public readonly string $email, + private string $password + ) { + } +} +``` + +### Enums (PHP 8.1+) + +```php +// Bad - class constants +class OrderStatus +{ + public const PENDING = 'pending'; + public const PROCESSING = 'processing'; + public const COMPLETED = 'completed'; + public const CANCELLED = 'cancelled'; +} + +// Good - enum +enum OrderStatus: string +{ + case PENDING = 'pending'; + case PROCESSING = 'processing'; + case COMPLETED = 'completed'; + case CANCELLED = 'cancelled'; + + public function label(): string + { + return match($this) { + self::PENDING => 'Pending', + self::PROCESSING => 'Processing', + self::COMPLETED => 'Completed', + self::CANCELLED => 'Cancelled', + }; + } +} +``` + +### Interface Segregation + +```php +// Bad - fat interface +interface UserServiceInterface +{ + public function create(array $data): User; + public function update(int $id, array $data): User; + public function delete(int $id): void; + public function sendWelcomeEmail(User $user): void; + public function sendPasswordReset(User $user): void; + public function generateReport(): array; +} + +// Good - segregated interfaces +interface UserRepositoryInterface +{ + public function create(array $data): User; + public function update(int $id, array $data): User; + public function delete(int $id): void; +} + +interface UserNotificationInterface +{ + public function sendWelcomeEmail(User $user): void; + public function sendPasswordReset(User $user): void; +} + +interface UserReportInterface +{ + public function generateReport(): array; +} +``` + +### Dependency Injection + +```php +// Bad - hard-coded dependencies +class OrderService +{ + public function createOrder(array $data): Order + { + $repository = new OrderRepository(); + $emailService = new EmailService(); + + $order = $repository->create($data); + $emailService->sendConfirmation($order); + + return $order; + } +} + +// Good - injected dependencies +class OrderService +{ + public function __construct( + private OrderRepository $repository, + private EmailService $emailService + ) { + } + + public function createOrder(array $data): Order + { + $order = $this->repository->create($data); + $this->emailService->sendConfirmation($order); + + return $order; + } +} +``` + +--- + +## PHP Simplification Checklist + +- [ ] `declare(strict_types=1)` at the top of files +- [ ] All functions have explicit return types +- [ ] All parameters have type hints +- [ ] Using constructor property promotion where applicable +- [ ] Using match expressions instead of switch when + appropriate +- [ ] Using nullsafe operator for chained null checks +- [ ] Using exceptions instead of error codes +- [ ] Using array functions (map, filter, reduce) + appropriately +- [ ] Using modern PHP features (8.0+) when available +- [ ] Following PSR-12 coding standards +- [ ] Proper namespace organization +- [ ] No unused imports + +--- + +**PHP Version Recommendation**: Use PHP 8.1+ for best modern +features and performance. diff --git a/code-simplifier/steering/python-django.md b/code-simplifier/steering/python-django.md new file mode 100644 index 0000000..8926104 --- /dev/null +++ b/code-simplifier/steering/python-django.md @@ -0,0 +1,466 @@ +# Django Code Simplification Guide + +This guide covers Django-specific patterns, conventions, +and best practices for writing elegant, maintainable code. + +## Table of Contents + +1. [Django Conventions](#django-conventions) +2. [Models and QuerySet Optimization](#models-and-queryset-optimization) +3. [Views (CBV vs FBV)](#views-cbv-vs-fbv) +4. [Forms](#forms) +5. [Templates](#templates) +6. [Middleware](#middleware) +7. [Signals](#signals) +8. [Testing](#testing) +9. [Common Patterns](#common-patterns) + +--- + +## Django Conventions + +### Project Structure + +Follow Django's standard project structure and keep apps cohesive: + +``` +config/ +├── settings/ +│ ├── base.py +│ ├── dev.py +│ └── prod.py +├── urls.py +└── wsgi.py +apps/ +├── users/ +├── orders/ +└── billing/ +manage.py +``` + +### Naming Conventions + +```python +# Apps - plural or domain-focused +users, orders, billing + +# Models - singular, PascalCase +User, Order, OrderItem + +# Database tables - app_label_modelname +users_user, orders_order + +# URLs - nouns, plural resources +path("orders/", order_list) +path("orders//", order_detail) + +# Templates - app namespace folders +orders/order_list.html +orders/order_detail.html +``` + +--- + +## Models and QuerySet Optimization + +### Model Organization + +```python +from django.db import models +from django.utils import timezone + +class Order(models.Model): + # 1. Constants + STATUS_PENDING = "pending" + STATUS_COMPLETED = "completed" + STATUS_CHOICES = [ + (STATUS_PENDING, "Pending"), + (STATUS_COMPLETED, "Completed"), + ] + + # 2. Fields + user = models.ForeignKey("users.User", on_delete=models.CASCADE) + total = models.DecimalField(max_digits=10, decimal_places=2) + status = models.CharField(max_length=20, choices=STATUS_CHOICES) + completed_at = models.DateTimeField(null=True, blank=True) + + # 3. Manager / QuerySet + class QuerySet(models.QuerySet): + def completed(self): + return self.filter(status=Order.STATUS_COMPLETED) + + def recent(self, days=30): + cutoff = timezone.now() - timezone.timedelta(days=days) + return self.filter(created_at__gte=cutoff) + + objects = QuerySet.as_manager() + + # 4. Business Logic + def mark_completed(self): + self.status = self.STATUS_COMPLETED + self.completed_at = timezone.now() + self.save(update_fields=["status", "completed_at"]) + + def is_completed(self): + return self.status == self.STATUS_COMPLETED +``` + +### Avoid Fat Queries in Views + +```python +# Bad - repeated query logic +completed_orders = Order.objects.filter(status="completed", user=request.user) +recent_orders = Order.objects.filter(created_at__gte=timezone.now() - timezone.timedelta(days=30)) + +# Good - reusable QuerySet methods +completed_orders = Order.objects.completed().filter(user=request.user) +recent_orders = Order.objects.recent(30) +``` + +### QuerySet Optimization + +```python +# Bad - N+1 queries +orders = Order.objects.all() +for order in orders: + print(order.user.email) + +# Good - select_related for FK +orders = Order.objects.select_related("user").all() +for order in orders: + print(order.user.email) + +# Good - prefetch_related for M2M or reverse FK +orders = Order.objects.prefetch_related("items__product").all() + +# Good - defer or only for large tables +users = User.objects.only("id", "email") +``` + +### Bulk Operations + +```python +# Bad - save in a loop +for item in items: + OrderItem.objects.create(order=order, **item) + +# Good - bulk_create +OrderItem.objects.bulk_create( + [OrderItem(order=order, **item) for item in items] +) +``` + +--- + +## Views (CBV vs FBV) + +### Prefer CBVs for CRUD + +```python +# Bad - bloated FBV with mixed concerns +@login_required +def order_detail(request, pk): + order = get_object_or_404(Order, pk=pk, user=request.user) + if request.method == "POST": + form = OrderNoteForm(request.POST) + if form.is_valid(): + form.save(order=order) + return redirect("orders:detail", pk=order.pk) + else: + form = OrderNoteForm() + return render(request, "orders/order_detail.html", {"order": order, "form": form}) + +# Good - CBV with clear separation +class OrderDetailView(LoginRequiredMixin, DetailView): + model = Order + template_name = "orders/order_detail.html" + + def get_queryset(self): + return Order.objects.filter(user=self.request.user) + +class OrderNoteCreateView(LoginRequiredMixin, CreateView): + form_class = OrderNoteForm + + def form_valid(self, form): + form.instance.order_id = self.kwargs["pk"] + return super().form_valid(form) +``` + +### Prefer FBVs for Simple Endpoints + +```python +# Good - concise FBV for a simple endpoint +@require_POST +def order_archive(request, pk): + Order.objects.filter(pk=pk, user=request.user).update(status=Order.STATUS_COMPLETED) + return redirect("orders:list") +``` + +--- + +## Forms + +### Keep Validation in Forms + +```python +# Bad - validation in the view +if request.POST.get("quantity", 0) < 1: + form.add_error("quantity", "Must be at least 1") + +# Good - validation in the form +class OrderItemForm(forms.ModelForm): + class Meta: + model = OrderItem + fields = ["product", "quantity"] + + def clean_quantity(self): + quantity = self.cleaned_data["quantity"] + if quantity < 1: + raise forms.ValidationError("Must be at least 1") + return quantity +``` + +### Prefer ModelForm for CRUD + +```python +# Bad - manual assignment +order = Order() +order.user = request.user +order.total = form.cleaned_data["total"] +order.save() + +# Good - use ModelForm with commit=False +order = form.save(commit=False) +order.user = request.user +order.save() +``` + +--- + +## Templates + +### Keep Templates Logic-Light + +```django +{# Bad - heavy logic in template #} +{% for order in orders %} + {% if order.status == "completed" and order.total > 100 %} +
  • {{ order.id }} - VIP
  • + {% endif %} +{% endfor %} + +{# Good - precompute in view or manager #} +{% for order in vip_orders %} +
  • {{ order.id }} - VIP
  • +{% endfor %} +``` + +### Use Template Tags and Filters + +```python +# Good - reusable template filter +from django import template + +register = template.Library() + +@register.filter +def currency(value): + return f"${value:,.2f}" +``` + +```django +{{ order.total|currency }} +``` + +--- + +## Middleware + +### Keep Middleware Focused + +```python +# Bad - middleware doing multiple responsibilities +class RequestMiddleware: + def __call__(self, request): + request.start = timezone.now() + request.user_agent = request.META.get("HTTP_USER_AGENT") + if request.user.is_authenticated: + request.user.last_seen = timezone.now() + request.user.save(update_fields=["last_seen"]) + response = self.get_response(request) + response["X-Request-Time"] = str(timezone.now() - request.start) + return response + +# Good - single responsibility middleware +class RequestTimingMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + start = timezone.now() + response = self.get_response(request) + response["X-Request-Time"] = str(timezone.now() - start) + return response +``` + +--- + +## Signals + +### Use Signals Sparingly + +```python +# Bad - hidden side effects everywhere +@receiver(post_save, sender=Order) +def send_order_email(sender, instance, created, **kwargs): + if created: + send_email(instance.user.email) + +# Good - explicit orchestration in service +class OrderService: + def create_order(self, user, items): + order = Order.objects.create(user=user, total=0) + OrderItem.objects.bulk_create( + [OrderItem(order=order, **item) for item in items] + ) + send_email(user.email) + return order +``` + +### Good Signal Use Cases + +```python +# Good - decoupled audit logging +@receiver(post_save, sender=Order) +def log_order_change(sender, instance, created, **kwargs): + action = "created" if created else "updated" + AuditLog.objects.create( + entity="order", + entity_id=instance.id, + action=action, + ) +``` + +--- + +## Testing + +### Use Factories and Fixtures + +```python +# Bad - verbose setup in every test +user = User.objects.create(email="user@example.com") +order = Order.objects.create(user=user, total=100) + +# Good - factory usage +user = UserFactory() +order = OrderFactory(user=user, total=100) +``` + +### Use Client and RequestFactory Appropriately + +```python +# Good - integration-style test +response = client.get("/orders/") +assert response.status_code == 200 + +# Good - unit-style view test +request = rf.get("/orders/") +response = OrderListView.as_view()(request) +assert response.status_code == 200 +``` + +### Use pytest-django or Django TestCase + +```python +# Good - pytest fixture style +@pytest.mark.django_db +def test_order_total_calculation(order_factory): + order = order_factory(total=50) + assert order.total == 50 + +# Good - Django TestCase +class OrderModelTests(TestCase): + def test_mark_completed(self): + order = OrderFactory() + order.mark_completed() + self.assertEqual(order.status, Order.STATUS_COMPLETED) +``` + +--- + +## Common Patterns + +### Service Layer for Business Logic + +```python +# Bad - business logic in view +class OrderCreateView(CreateView): + def form_valid(self, form): + order = form.save() + for item in self.request.POST.getlist("items"): + OrderItem.objects.create(order=order, product_id=item) + send_email(order.user.email) + return super().form_valid(form) + +# Good - service class +class OrderService: + def create_order(self, user, items): + order = Order.objects.create(user=user, total=0) + OrderItem.objects.bulk_create( + [OrderItem(order=order, product_id=item) for item in items] + ) + send_email(user.email) + return order +``` + +### Class-Based Views Mixins + +```python +# Good - mixins for shared behavior +class StaffRequiredMixin(UserPassesTestMixin): + def test_func(self): + return self.request.user.is_staff + +class OrderAdminListView(StaffRequiredMixin, ListView): + model = Order +``` + +### Repository Style for Complex Queries + +```python +# Good - simple repository wrapper +class OrderRepository: + def find_with_items(self, order_id): + return Order.objects.select_related("user").prefetch_related("items").get(id=order_id) + + def recent_for_user(self, user, days=30): + return Order.objects.filter(user=user).recent(days) +``` + +--- + +## Django Simplification Checklist + +- [ ] Apps are organized by domain with clear boundaries +- [ ] Query logic is in QuerySet/Manager methods +- [ ] select_related/prefetch_related used to prevent N+1 +- [ ] Views are thin; use CBVs for CRUD +- [ ] Forms handle validation and normalization +- [ ] Templates are logic-light and use custom filters/tags +- [ ] Middleware is single-responsibility +- [ ] Signals are limited to decoupled side effects +- [ ] Tests use factories and focus on behavior +- [ ] Service layer for complex business logic +- [ ] Bulk operations for batch writes + +--- + +## Additional Resources + +- Django Documentation: https://docs.djangoproject.com/ +- Django Best Practices: https://github.com/HackSoftware/Django-Styleguide +- Two Scoops of Django: https://www.feldroy.com/books/two-scoops-of-django + +**Django Version Recommendation**: Use Django 4.2+ or 5.x with Python +3.10+ for best features and support. diff --git a/code-simplifier/steering/python-fastapi.md b/code-simplifier/steering/python-fastapi.md new file mode 100644 index 0000000..d42efba --- /dev/null +++ b/code-simplifier/steering/python-fastapi.md @@ -0,0 +1,364 @@ +# FastAPI Code Simplification Guide + +This guide covers FastAPI-specific patterns, conventions, +and best practices for writing clean, maintainable code. + +## Table of Contents + +1. [FastAPI Conventions](#fastapi-conventions) +2. [Dependency Injection](#dependency-injection) +3. [Path Operations](#path-operations) +4. [Pydantic Models](#pydantic-models) +5. [Async Patterns](#async-patterns) +6. [Database Integration](#database-integration) +7. [Middleware](#middleware) +8. [Testing](#testing) +9. [OpenAPI Documentation](#openapi-documentation) + +--- + +## FastAPI Conventions + +### Directory Structure + +Keep modules aligned with features and layers: + +``` +app/ +├── main.py +├── api/ +│ ├── deps.py +│ ├── routes/ +│ └── schemas/ +├── core/ +│ ├── config.py +│ └── logging.py +├── db/ +│ ├── session.py +│ └── models/ +├── services/ +└── tests/ +``` + +### Naming Conventions + +```python +# Routers - resource focused +orders.py, users.py + +# Services - behavior focused +order_service.py, billing_service.py + +# Schemas - explicit type role +CreateOrderRequest, OrderResponse, UserSummary + +# Database models - singular +Order, User, OrderItem +``` + +--- + +## Dependency Injection + +### Prefer Explicit Dependencies + +```python +# Bad - hidden global dependency +db = SessionLocal() + +@app.post("/orders") +def create_order(payload: CreateOrderRequest): + return OrderService(db).create(payload) + +# Good - explicit dependency injection +def get_db() -> Generator[Session, None, None]: + db = SessionLocal() + try: + yield db + finally: + db.close() + +@router.post("/orders") +def create_order( + payload: CreateOrderRequest, + db: Session = Depends(get_db), +) -> OrderResponse: + return OrderService(db).create(payload) +``` + +### Use Dependency Factories for Swappable Implementations + +```python +# Good - interface-like protocol for easy testing +class PaymentGateway(Protocol): + def charge(self, request: PaymentRequest) -> PaymentResult: + ... + +class StripePaymentGateway: + def charge(self, request: PaymentRequest) -> PaymentResult: + return PaymentResult.success() + +def get_payment_gateway() -> PaymentGateway: + return StripePaymentGateway() +``` + +--- + +## Path Operations + +### Thin Handlers + +```python +# Bad - validation, logic, and persistence in handler +@router.post("/orders") +def create_order(payload: dict, db: Session = Depends(get_db)): + if "items" not in payload or not payload["items"]: + raise HTTPException(status_code=400, detail="No items") + total = sum(i["price"] * i["quantity"] for i in payload["items"]) + order = Order(status="PENDING", total=total) + db.add(order) + db.commit() + db.refresh(order) + return order + +# Good - handler delegates to service, validation in schema +@router.post("/orders", response_model=OrderResponse, status_code=201) +def create_order( + payload: CreateOrderRequest, + service: OrderService = Depends(get_order_service), +) -> OrderResponse: + return service.create(payload) +``` + +### Response Models Over ORM Objects + +```python +# Bad - returning ORM objects directly +@router.get("/orders/{order_id}") +def get_order(order_id: int, db: Session = Depends(get_db)): + return db.get(Order, order_id) + +# Good - map to response schema +@router.get("/orders/{order_id}", response_model=OrderResponse) +def get_order(order_id: int, service: OrderService = Depends(get_order_service)): + return service.get(order_id) +``` + +--- + +## Pydantic Models + +### Separate Request and Response Types + +```python +# Bad - one schema for everything +class OrderSchema(BaseModel): + id: int | None = None + status: str + total: float + +# Good - explicit request/response schemas +class CreateOrderRequest(BaseModel): + items: list[OrderItemRequest] + +class OrderResponse(BaseModel): + id: int + status: str + total: Decimal +``` + +### Validation at the Edge + +```python +# Good - validate types and ranges in schema +class OrderItemRequest(BaseModel): + sku: str + quantity: int = Field(gt=0) + price: Decimal = Field(gt=Decimal("0.00")) +``` + +--- + +## Async Patterns + +### Do Not Block the Event Loop + +```python +# Bad - blocking call in async handler +@router.get("/reports/{report_id}") +async def get_report(report_id: str): + data = generate_report_blocking(report_id) + return {"data": data} + +# Good - use threadpool for blocking work +@router.get("/reports/{report_id}") +async def get_report(report_id: str): + data = await run_in_threadpool(generate_report_blocking, report_id) + return {"data": data} +``` + +### Use Async I/O End-to-End + +```python +# Bad - async handler with sync DB session +@router.get("/orders/{order_id}") +async def get_order(order_id: int, db: Session = Depends(get_db)): + return db.get(Order, order_id) + +# Good - async handler with async DB session +@router.get("/orders/{order_id}") +async def get_order(order_id: int, db: AsyncSession = Depends(get_async_db)): + return await db.get(Order, order_id) +``` + +--- + +## Database Integration + +### Keep Session Lifecycle Scoped + +```python +# Bad - session created at import time +db = SessionLocal() + +def get_order(order_id: int): + return db.get(Order, order_id) + +# Good - session per request +def get_db() -> Generator[Session, None, None]: + db = SessionLocal() + try: + yield db + finally: + db.close() +``` + +### Prefer Explicit Transactions + +```python +# Bad - multiple commits for one workflow +def fulfill_order(order_id: int, db: Session): + order = db.get(Order, order_id) + order.status = "PAID" + db.commit() + reserve_inventory(order, db) + db.commit() + +# Good - one transaction boundary +def fulfill_order(order_id: int, db: Session): + with db.begin(): + order = db.get(Order, order_id) + order.status = "PAID" + reserve_inventory(order, db) +``` + +--- + +## Middleware + +### Keep Middleware Focused + +```python +# Bad - middleware doing auth, logging, and parsing +@app.middleware("http") +async def all_in_one(request: Request, call_next): + user = await auth(request) + request.state.user = user + request.state.body = await request.json() + response = await call_next(request) + response.headers["X-Request-Id"] = str(uuid4()) + return response + +# Good - small, single-purpose middleware +@app.middleware("http") +async def request_id_middleware(request: Request, call_next): + request.state.request_id = str(uuid4()) + response = await call_next(request) + response.headers["X-Request-Id"] = request.state.request_id + return response +``` + +--- + +## Testing + +### Use TestClient and Dependency Overrides + +```python +# Bad - hitting real database in unit tests +def test_create_order(): + response = client.post("/orders", json={"items": [{"sku": "x", "quantity": 1, "price": 10}]}) + assert response.status_code == 201 + +# Good - override dependencies for isolation +def test_create_order(client: TestClient, db_session: Session): + app.dependency_overrides[get_db] = lambda: db_session + response = client.post("/orders", json={"items": [{"sku": "x", "quantity": 1, "price": 10}]}) + assert response.status_code == 201 +``` + +### Async Tests with AnyIO + +```python +@pytest.mark.anyio +async def test_get_order(async_client: AsyncClient): + response = await async_client.get("/orders/1") + assert response.status_code == 200 +``` + +--- + +## OpenAPI Documentation + +### Use Response Models and Metadata + +```python +# Bad - minimal metadata, no response model +@router.get("/orders/{order_id}") +def get_order(order_id: int): + ... + +# Good - clear docs, tags, summaries, response models +@router.get( + "/orders/{order_id}", + response_model=OrderResponse, + summary="Get order by id", + tags=["orders"], +) +def get_order(order_id: int, service: OrderService = Depends(get_order_service)): + return service.get(order_id) +``` + +### Group Routers + +```python +# Good - group endpoints by router and include tags +orders_router = APIRouter(prefix="/orders", tags=["orders"]) +users_router = APIRouter(prefix="/users", tags=["users"]) +``` + +--- + +## FastAPI Simplification Checklist + +- [ ] Routers are thin and delegate to services +- [ ] Dependencies are explicit via Depends +- [ ] Request/response schemas are separate +- [ ] Async endpoints avoid blocking calls +- [ ] DB sessions are scoped per request +- [ ] Transactions wrap multi-step updates +- [ ] Middleware is single-purpose +- [ ] Tests use dependency overrides +- [ ] OpenAPI metadata is filled in + +--- + +## Additional Resources + +- FastAPI Documentation: https://fastapi.tiangolo.com +- Pydantic Documentation: https://docs.pydantic.dev +- SQLAlchemy Documentation: https://docs.sqlalchemy.org +- Testing FastAPI: https://fastapi.tiangolo.com/tutorial/testing + +**FastAPI Version Recommendation**: Use FastAPI 0.110+ +with Pydantic v2 and Python 3.11+. diff --git a/code-simplifier/steering/python-fastmcp.md b/code-simplifier/steering/python-fastmcp.md new file mode 100644 index 0000000..5d99dd1 --- /dev/null +++ b/code-simplifier/steering/python-fastmcp.md @@ -0,0 +1,442 @@ +# FastMCP Python Code Simplification Guide + +This guide focuses on writing clean, maintainable MCP servers using FastMCP. +It highlights common simplification patterns, error handling, testing, and best +practices, with side-by-side examples of bad vs good approaches. The patterns +align with typical FastMCP documentation conventions such as `@mcp.tool`, +`@mcp.resource`, `@mcp.prompt`, and server lifespan hooks. + +## Table of Contents + +1. [Server Patterns](#server-patterns) +2. [Tool Definitions](#tool-definitions) +3. [Resource Management](#resource-management) +4. [Context Handling](#context-handling) +5. [Error Handling](#error-handling) +6. [Testing MCP Servers](#testing-mcp-servers) +7. [Best Practices](#best-practices) +8. [Checklist](#checklist) +9. [Resources](#resources) + +--- + +## Server Patterns + +### Minimal, Clear Server Construction + +```python +# Bad - global state, hidden configuration, hard to test +from fastmcp import FastMCP + +mcp = FastMCP("server") +API_KEY = "dev-key" + +@mcp.tool() +def search(query: str) -> str: + return f"Searching {query} with {API_KEY}" +``` + +```python +# Good - explicit config, pure logic separated from transport +from dataclasses import dataclass +from fastmcp import FastMCP + +@dataclass(frozen=True) +class Config: + api_key: str + +def build_server(config: Config) -> FastMCP: + mcp = FastMCP("search-server") + + @mcp.tool() + def search(query: str) -> str: + return f"Searching {query} with {config.api_key}" + + return mcp +``` + +### Lifespan and Startup/Shutdown Hooks + +```python +# Bad - setup scattered inside tools +from fastmcp import FastMCP + +mcp = FastMCP("server") + +@mcp.tool() +def fetch_user(user_id: str) -> dict: + db = connect_db() + return db.get_user(user_id) +``` + +```python +# Good - use lifespan hooks for shared resources +from fastmcp import FastMCP + +mcp = FastMCP("server") + +@mcp.lifespan() +async def lifespan(): + db = await connect_db_async() + try: + yield {"db": db} + finally: + await db.close() + +@mcp.tool() +def fetch_user(user_id: str, ctx) -> dict: + return ctx.state["db"].get_user(user_id) +``` + +--- + +## Tool Definitions + +### Keep Tool Functions Narrow and Typed + +```python +# Bad - multiple responsibilities, weak types +from fastmcp import FastMCP + +mcp = FastMCP("server") + +@mcp.tool() +def manage_user(action: str, payload: dict) -> dict: + if action == "create": + return create_user(payload) + if action == "update": + return update_user(payload) + raise ValueError("Unknown action") +``` + +```python +# Good - single purpose, typed inputs/outputs +from dataclasses import dataclass +from fastmcp import FastMCP + +mcp = FastMCP("server") + +@dataclass +class UserInput: + name: str + email: str + +@mcp.tool() +def create_user_tool(user: UserInput) -> dict: + return create_user(user) + +@mcp.tool() +def update_user_tool(user_id: str, user: UserInput) -> dict: + return update_user(user_id, user) +``` + +### Document Tools With Descriptions + +```python +# Bad - missing descriptions, unclear intent +@mcp.tool() +def summarize(text: str) -> str: + return do_summary(text) +``` + +```python +# Good - explicit docstring and clear parameter names +@mcp.tool() +def summarize(text: str) -> str: + """Summarize a block of text into a short paragraph.""" + return do_summary(text) +``` + +--- + +## Resource Management + +FastMCP resources (via `@mcp.resource`) provide structured access to data. + +### Avoid Dynamic, Unbounded Resource Paths + +```python +# Bad - single resource with too many responsibilities +from fastmcp import FastMCP + +mcp = FastMCP("server") + +@mcp.resource("data/{path}") +def data_resource(path: str) -> str: + return read_anything(path) +``` + +```python +# Good - explicit, focused resource endpoints +from fastmcp import FastMCP + +mcp = FastMCP("server") + +@mcp.resource("data/users/{user_id}") +def user_resource(user_id: str) -> dict: + return load_user(user_id) + +@mcp.resource("data/orders/{order_id}") +def order_resource(order_id: str) -> dict: + return load_order(order_id) +``` + +### Cache or Reuse Expensive Resources + +```python +# Bad - reload config on every call +@mcp.resource("config") +def config_resource() -> dict: + return load_config_from_disk() +``` + +```python +# Good - read once, reuse from context +@mcp.lifespan() +async def lifespan(): + config = load_config_from_disk() + yield {"config": config} + +@mcp.resource("config") +def config_resource(ctx) -> dict: + return ctx.state["config"] +``` + +--- + +## Context Handling + +Use context (`ctx`) to access shared state, logging, and request metadata. + +### Keep Context Usage Explicit + +```python +# Bad - implicit global context +CURRENT_REQUEST = None + +@mcp.tool() +def get_request_id() -> str: + return CURRENT_REQUEST.id +``` + +```python +# Good - explicit context passing +@mcp.tool() +def get_request_id(ctx) -> str: + return ctx.request_id +``` + +### Avoid Passing Context Deep into Pure Functions + +```python +# Bad - context leaks into pure business logic +def calculate_quote(ctx, user_id: str) -> dict: + logger = ctx.logger + logger.info("Calculating quote") + return pricing_engine(user_id) +``` + +```python +# Good - extract data once, keep logic pure +def calculate_quote(user_id: str) -> dict: + return pricing_engine(user_id) + +@mcp.tool() +def calculate_quote_tool(user_id: str, ctx) -> dict: + ctx.logger.info("Calculating quote") + return calculate_quote(user_id) +``` + +--- + +## Error Handling + +Prefer clear, user-safe error messages while preserving internal detail for logs. + +### Avoid Leaking Internal Exceptions + +```python +# Bad - raw exception details exposed +@mcp.tool() +def parse_report(payload: dict) -> dict: + return parse(payload) # May raise with stack trace +``` + +```python +# Good - catch and wrap, log internally +@mcp.tool() +def parse_report(payload: dict, ctx) -> dict: + try: + return parse(payload) + except ValueError as exc: + ctx.logger.exception("Parse failure") + raise ValueError("Invalid report payload") from exc +``` + +### Validate Inputs Early + +```python +# Bad - validation buried later +@mcp.tool() +def create_invoice(amount: float) -> dict: + return invoice_service.create(amount) +``` + +```python +# Good - guardrails at boundaries +@mcp.tool() +def create_invoice(amount: float) -> dict: + if amount <= 0: + raise ValueError("Amount must be positive") + return invoice_service.create(amount) +``` + +--- + +## Testing MCP Servers + +Keep tools pure and testable with minimal FastMCP plumbing. + +### Test Pure Logic Directly + +```python +# Bad - tests depend on server infrastructure +def test_search_tool(mcp_client): + result = mcp_client.call_tool("search", {"query": "cats"}) + assert "cats" in result +``` + +```python +# Good - separate pure logic and test directly +def search_logic(query: str, api_key: str) -> str: + return f"Searching {query} with {api_key}" + +def test_search_logic(): + result = search_logic("cats", "test-key") + assert "cats" in result +``` + +### Use FastMCP Test Clients When Needed + +```python +# Good - integration test around tool wiring +def test_search_tool_integration(mcp_client): + result = mcp_client.call_tool("search", {"query": "cats"}) + assert "cats" in result +``` + +### Fake External Dependencies + +```python +# Bad - hitting live services +def test_weather_tool(mcp_client): + result = mcp_client.call_tool("weather", {"city": "Paris"}) + assert "Paris" in result +``` + +```python +# Good - stub service with predictable output +class FakeWeatherService: + def get(self, city: str) -> dict: + return {"city": city, "temp_c": 18} + +def test_weather_logic(): + service = FakeWeatherService() + assert service.get("Paris")["temp_c"] == 18 +``` + +--- + +## Best Practices + +### Keep Tools Small and Composable + +```python +# Bad - tool does fetch, transform, and persist +@mcp.tool() +def sync_users() -> dict: + data = api.fetch_users() + transformed = transform_users(data) + return db.save_users(transformed) +``` + +```python +# Good - delegate to small, testable functions +def fetch_users(): + return api.fetch_users() + +def transform_users(data): + return [normalize_user(u) for u in data] + +def save_users(users): + return db.save_users(users) + +@mcp.tool() +def sync_users() -> dict: + users = transform_users(fetch_users()) + return save_users(users) +``` + +### Avoid Hidden Side Effects + +```python +# Bad - tool mutates global config +@mcp.tool() +def set_timezone(tz: str) -> str: + global SETTINGS + SETTINGS["timezone"] = tz + return "ok" +``` + +```python +# Good - update explicit state or config store +@mcp.tool() +def set_timezone(tz: str, ctx) -> str: + ctx.state["settings"].set_timezone(tz) + return "ok" +``` + +### Prefer Data Classes or Typed Models + +```python +# Bad - loose dicts +@mcp.tool() +def create_project(payload: dict) -> dict: + return projects.create(payload) +``` + +```python +# Good - explicit schema +from dataclasses import dataclass + +@dataclass +class ProjectInput: + name: str + owner_id: str + +@mcp.tool() +def create_project(project: ProjectInput) -> dict: + return projects.create(project) +``` + +--- + +## Checklist + +- Define a clear `build_server(config)` entry for testability. +- Keep `@mcp.tool` functions single-purpose and typed. +- Use `@mcp.resource` for structured data access, avoid catch-all paths. +- Use lifespan hooks to manage shared resources like DBs or caches. +- Keep context access explicit, avoid passing `ctx` deep into pure logic. +- Validate inputs at tool boundaries; wrap and log errors safely. +- Separate pure logic from IO for fast unit tests. +- Use FastMCP test clients for integration-level coverage. + +--- + +## Resources + +- FastMCP README and examples (decorators: `@mcp.tool`, `@mcp.resource`, `@mcp.prompt`) +- MCP specification and protocol reference +- Python typing and `dataclasses` documentation +- Testing patterns for async servers (pytest, asyncio) diff --git a/code-simplifier/steering/python-flask.md b/code-simplifier/steering/python-flask.md new file mode 100644 index 0000000..ec3412a --- /dev/null +++ b/code-simplifier/steering/python-flask.md @@ -0,0 +1,422 @@ +# Flask Code Simplification Guide + +This guide covers Flask-specific patterns, conventions, +and best practices for writing clean, maintainable code. + +## Table of Contents + +1. [Flask Conventions](#flask-conventions) +2. [Blueprints](#blueprints) +3. [Application Factory Pattern](#application-factory-pattern) +4. [Request and Response Handling](#request-and-response-handling) +5. [SQLAlchemy Patterns](#sqlalchemy-patterns) +6. [Configuration](#configuration) +7. [Error Handling](#error-handling) +8. [Testing](#testing) + +--- + +## Flask Conventions + +### Directory Structure + +Follow a modular structure with clear separation of concerns: + +``` +app/ +├── __init__.py +├── extensions.py +├── config.py +├── blueprints/ +│ ├── users/ +│ │ ├── __init__.py +│ │ ├── routes.py +│ │ ├── schemas.py +│ │ └── services.py +│ └── orders/ +│ ├── __init__.py +│ ├── routes.py +│ ├── schemas.py +│ └── services.py +├── models/ +│ ├── __init__.py +│ ├── user.py +│ └── order.py +├── errors/ +│ ├── __init__.py +│ └── handlers.py +└── tests/ + ├── conftest.py + └── test_users.py +``` + +### Naming Conventions + +```python +# Blueprints - plural, feature-based +users_bp = Blueprint("users", __name__) +orders_bp = Blueprint("orders", __name__) + +# Routes - clear resource naming +@users_bp.get("/users/") +def get_user(user_id): ... + +# Models - singular, PascalCase +class User(db.Model): ... + +# Services - singular, PascalCase +class UserService: ... +``` + +--- + +## Blueprints + +### Keep Blueprints Focused + +```python +# Bad - single blueprint with unrelated routes +api = Blueprint("api", __name__) + +@api.get("/users/") +def get_user(user_id): ... + +@api.get("/orders/") +def get_order(order_id): ... + +# Good - separate blueprints by feature +users_bp = Blueprint("users", __name__) +orders_bp = Blueprint("orders", __name__) + +@users_bp.get("/users/") +def get_user(user_id): ... + +@orders_bp.get("/orders/") +def get_order(order_id): ... +``` + +### Blueprint Registration + +```python +# app/__init__.py +def register_blueprints(app): + from app.blueprints.users import users_bp + from app.blueprints.orders import orders_bp + + app.register_blueprint(users_bp, url_prefix="/api") + app.register_blueprint(orders_bp, url_prefix="/api") +``` + +--- + +## Application Factory Pattern + +### Use a Factory for Configurable Apps + +```python +# Bad - global app instance at import time +app = Flask(__name__) +app.config.from_object("app.config.Config") + +@app.get("/health") +def health(): + return {"status": "ok"} + +# Good - factory pattern with extensions +def create_app(config_object="app.config.Config"): + app = Flask(__name__) + app.config.from_object(config_object) + + from app.extensions import db, migrate + db.init_app(app) + migrate.init_app(app, db) + + from app.errors import register_error_handlers + register_error_handlers(app) + + from app import register_blueprints + register_blueprints(app) + + return app +``` + +### Extension Initialization + +```python +# app/extensions.py +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + +db = SQLAlchemy() +migrate = Migrate() +``` + +--- + +## Request and Response Handling + +### Validate and Normalize Inputs + +```python +# Bad - manual parsing, no validation +@users_bp.post("/users") +def create_user(): + data = request.json + user = User(name=data["name"], email=data["email"]) + db.session.add(user) + db.session.commit() + return jsonify(user.to_dict()), 201 + +# Good - schema validation and explicit response +@users_bp.post("/users") +def create_user(): + payload = request.get_json() or {} + data = UserCreateSchema().load(payload) + + user = UserService().create_user(data) + return UserSchema().dump(user), 201 +``` + +### Keep Routes Thin + +```python +# Bad - business logic in route +@orders_bp.post("/orders") +def create_order(): + data = request.get_json() or {} + total = sum(item["price"] * item["qty"] for item in data["items"]) + order = Order(user_id=data["user_id"], total=total) + db.session.add(order) + db.session.commit() + return {"id": order.id}, 201 + +# Good - delegate to service layer +@orders_bp.post("/orders") +def create_order(): + data = OrderCreateSchema().load(request.get_json() or {}) + order = OrderService().create_order(data) + return OrderSchema().dump(order), 201 +``` + +--- + +## SQLAlchemy Patterns + +### Model Organization + +```python +class Order(db.Model): + __tablename__ = "orders" + + STATUS_PENDING = "pending" + STATUS_COMPLETED = "completed" + + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) + total = db.Column(db.Numeric(10, 2), nullable=False) + status = db.Column(db.String(20), default=STATUS_PENDING, nullable=False) + + user = db.relationship("User", back_populates="orders") + + @classmethod + def completed(cls): + return cls.query.filter_by(status=cls.STATUS_COMPLETED) + + def mark_completed(self): + self.status = self.STATUS_COMPLETED +``` + +### Avoid Query-in-Loop + +```python +# Bad - N+1 queries +orders = Order.query.all() +for order in orders: + print(order.user.email) + +# Good - eager loading +orders = Order.query.options(db.joinedload(Order.user)).all() +for order in orders: + print(order.user.email) +``` + +### Transactions for Multi-Step Operations + +```python +# Bad - partial writes on failure +order = Order(user_id=user.id, total=total) +db.session.add(order) +db.session.commit() + +for item in items: + db.session.add(OrderItem(order_id=order.id, **item)) +db.session.commit() + +# Good - atomic transaction +with db.session.begin(): + order = Order(user_id=user.id, total=total) + db.session.add(order) + for item in items: + db.session.add(OrderItem(order=order, **item)) +``` + +--- + +## Configuration + +### Environment-Based Settings + +```python +# app/config.py +import os + +class Config: + SQLALCHEMY_TRACK_MODIFICATIONS = False + JSON_SORT_KEYS = False + +class DevelopmentConfig(Config): + DEBUG = True + SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite:///dev.db") + +class TestingConfig(Config): + TESTING = True + SQLALCHEMY_DATABASE_URI = "sqlite://" + +class ProductionConfig(Config): + SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL") +``` + +### Config Selection + +```python +# wsgi.py +from app import create_app + +app = create_app("app.config.ProductionConfig") +``` + +--- + +## Error Handling + +### Centralized Error Handlers + +```python +# app/errors/handlers.py +from flask import jsonify +from werkzeug.exceptions import HTTPException + +def register_error_handlers(app): + @app.errorhandler(HTTPException) + def handle_http_exception(error): + return jsonify({ + "error": error.name, + "message": error.description, + }), error.code + + @app.errorhandler(Exception) + def handle_unexpected_exception(error): + app.logger.exception("Unhandled exception") + return jsonify({ + "error": "Internal Server Error", + "message": "An unexpected error occurred.", + }), 500 +``` + +### Custom Domain Errors + +```python +class ValidationError(Exception): + def __init__(self, message, details=None): + super().__init__(message) + self.details = details or {} + +# Register a handler for domain errors +@app.errorhandler(ValidationError) +def handle_validation_error(error): + return { + "error": "ValidationError", + "message": str(error), + "details": error.details, + }, 400 +``` + +--- + +## Testing + +### Use a Test App Factory + +```python +# tests/conftest.py +import pytest +from app import create_app +from app.extensions import db + +@pytest.fixture() +def app(): + app = create_app("app.config.TestingConfig") + with app.app_context(): + db.create_all() + yield app + db.drop_all() + +@pytest.fixture() +def client(app): + return app.test_client() +``` + +### Keep Tests Focused + +```python +# Bad - too much setup in each test +def test_create_user(client): + client.post("/users", json={"name": "A", "email": "a@example.com"}) + client.post("/users", json={"name": "B", "email": "b@example.com"}) + response = client.get("/users") + assert response.status_code == 200 + +# Good - fixtures + clear intent +@pytest.fixture() +def user(client): + response = client.post("/users", json={"name": "A", "email": "a@example.com"}) + return response.get_json() + +def test_list_users(client, user): + response = client.get("/users") + assert response.status_code == 200 + assert len(response.get_json()) == 1 +``` + +--- + +## Flask Simplification Checklist + +- [ ] Application factory used for app creation +- [ ] Blueprints split by feature +- [ ] Routes are thin; services handle business logic +- [ ] Input validation via schemas (Marshmallow or similar) +- [ ] SQLAlchemy models include helper methods/scopes +- [ ] Eager loading prevents N+1 queries +- [ ] Transactions wrap multi-step writes +- [ ] Environment-specific configuration classes +- [ ] Centralized error handling with consistent JSON responses +- [ ] Tests use fixtures and app factory +- [ ] Testing database is isolated and clean + +--- + +## Additional Resources + +- Flask Documentation: https://flask.palletsprojects.com +- Flask Application Factories: + https://flask.palletsprojects.com/en/latest/patterns/appfactories/ +- Flask Blueprints: + https://flask.palletsprojects.com/en/latest/blueprints/ +- Flask SQLAlchemy: + https://flask-sqlalchemy.palletsprojects.com +- Pytest: + https://docs.pytest.org + +**Flask Version Recommendation**: Use Flask 2.3+ with +Python 3.11+ for best features and support. diff --git a/code-simplifier/steering/python.md b/code-simplifier/steering/python.md new file mode 100644 index 0000000..ef8f385 --- /dev/null +++ b/code-simplifier/steering/python.md @@ -0,0 +1,540 @@ +# Python Code Simplification Guide + +This guide covers Python-specific simplification patterns +and best practices for writing Pythonic code. + +## Table of Contents + +1. [PEP 8 and Style](#pep-8-and-style) +2. [Pythonic Patterns](#pythonic-patterns) +3. [List Comprehensions](#list-comprehensions) +4. [Context Managers](#context-managers) +5. [Generators and Iterators](#generators-and-iterators) +6. [Type Hints](#type-hints) +7. [Error Handling](#error-handling) +8. [When to Load Django Guidance](#when-to-load-django-guidance) + +--- + +## PEP 8 and Style + +### Naming Conventions + +```python +# Variables and functions - snake_case +user_name = "John" +def calculate_total(items): + pass + +# Classes - PascalCase +class UserService: + pass + +# Constants - UPPER_SNAKE_CASE +MAX_RETRIES = 3 +DEFAULT_TIMEOUT = 30 + +# Private attributes/methods - leading underscore +class User: + def __init__(self): + self._password = None # Private + + def _validate(self): # Private method + pass +``` + +### Import Organization + +```python +# Standard library imports +import os +import sys +from datetime import datetime + +# Third-party imports +import requests + +# Local application imports +from .models import User +from .services import EmailService +``` + +### Line Length and Formatting + +```python +# Bad - too long +result = some_function(argument1, argument2, argument3, argument4, argument5, argument6) + +# Good - break into multiple lines +result = some_function( + argument1, + argument2, + argument3, + argument4, + argument5, + argument6 +) + +# Dictionary formatting +user_data = { + 'name': 'John Doe', + 'email': 'john@example.com', + 'age': 30, + 'is_active': True, +} +``` + +--- + +## Pythonic Patterns + +### The Zen of Python + +```python +import this + +# Key principles: +# - Explicit is better than implicit +# - Simple is better than complex +# - Readability counts +# - There should be one obvious way to do it +``` + +### EAFP vs LBYL + +```python +# Bad - LBYL (Look Before You Leap) +if key in dictionary: + value = dictionary[key] +else: + value = default_value + +# Good - EAFP (Easier to Ask for Forgiveness than Permission) +try: + value = dictionary[key] +except KeyError: + value = default_value + +# Even better - use dict.get() +value = dictionary.get(key, default_value) +``` + +### String Formatting + +```python +name = "John" +age = 30 + +# Bad - old style +message = "Hello, %s. You are %d years old." % (name, age) + +# Better - str.format() +message = "Hello, {}. You are {} years old.".format(name, age) + +# Best - f-strings (Python 3.6+) +message = f"Hello, {name}. You are {age} years old." + +# Complex expressions in f-strings +total = f"Total: ${sum(prices):.2f}" +``` + +### Unpacking + +```python +# Tuple unpacking +first, second = (1, 2) +first, *rest = [1, 2, 3, 4, 5] + +# Dictionary unpacking +defaults = {'theme': 'light', 'language': 'en'} +user_prefs = {'language': 'fr'} +config = {**defaults, **user_prefs} + +# Function arguments +def create_user(name, email, **kwargs): + user = User(name=name, email=email) + for key, value in kwargs.items(): + setattr(user, key, value) + return user +``` + +### Enumerate and Zip + +```python +# Bad - manual indexing +for i in range(len(items)): + print(f"{i}: {items[i]}") + +# Good - enumerate +for index, item in enumerate(items): + print(f"{index}: {item}") + +# Zip for parallel iteration +names = ['Alice', 'Bob', 'Charlie'] +ages = [25, 30, 35] + +for name, age in zip(names, ages): + print(f"{name} is {age} years old") +``` + +--- + +## List Comprehensions + +### Basic List Comprehensions + +```python +# Bad - manual loop +squares = [] +for x in range(10): + squares.append(x ** 2) + +# Good - list comprehension +squares = [x ** 2 for x in range(10)] + +# With condition +even_squares = [x ** 2 for x in range(10) if x % 2 == 0] + +# Nested comprehension +matrix = [[i * j for j in range(5)] for i in range(5)] +``` + +### Dictionary and Set Comprehensions + +```python +# Dictionary comprehension +user_dict = {user.id: user.name for user in users} + +# With condition +active_users = { + user.id: user.name + for user in users + if user.is_active +} + +# Set comprehension +unique_emails = {user.email.lower() for user in users} +``` + +### When NOT to Use Comprehensions + +```python +# Bad - too complex +result = [ + process_item(item) + for sublist in nested_list + for item in sublist + if item.is_valid() and item.status == 'active' + if not item.is_deleted +] + +# Good - explicit loop for clarity +result = [] +for sublist in nested_list: + for item in sublist: + if item.is_valid() and item.status == 'active': + if not item.is_deleted: + result.append(process_item(item)) +``` + +--- + +## Context Managers + +### Using Context Managers + +```python +# Bad - manual file handling +file = open('data.txt', 'r') +try: + data = file.read() +finally: + file.close() + +# Good - context manager +with open('data.txt', 'r') as file: + data = file.read() + +# Multiple context managers +with open('input.txt', 'r') as infile, open('output.txt', 'w') as outfile: + outfile.write(infile.read()) +``` + +### Creating Custom Context Managers + +```python +from contextlib import contextmanager + +# Using decorator +@contextmanager +def database_transaction(connection): + try: + yield connection + connection.commit() + except Exception: + connection.rollback() + raise + +# Usage +with database_transaction(conn) as db: + db.execute("INSERT INTO users ...") + +# Class-based context manager +class Timer: + def __enter__(self): + self.start = time.time() + return self + + def __exit__(self, *args): + self.end = time.time() + self.duration = self.end - self.start + print(f"Elapsed time: {self.duration:.2f}s") + +# Usage +with Timer(): + # Code to time + process_data() +``` + +--- + +## Generators and Iterators + +### Generator Functions + +```python +# Bad - creates entire list in memory +def get_numbers(n): + result = [] + for i in range(n): + result.append(i ** 2) + return result + +# Good - generator (lazy evaluation) +def get_numbers(n): + for i in range(n): + yield i ** 2 + +# Usage +for num in get_numbers(1000000): # Memory efficient + print(num) +``` + +### Generator Expressions + +```python +# List comprehension - creates entire list +squares = [x ** 2 for x in range(1000000)] + +# Generator expression - lazy evaluation +squares = (x ** 2 for x in range(1000000)) + +# Use in functions that accept iterables +total = sum(x ** 2 for x in range(1000000)) +``` + +### Itertools + +```python +from itertools import chain, groupby, islice + +# Chain multiple iterables +combined = chain(list1, list2, list3) + +# Group by key +data = [ + {'name': 'Alice', 'dept': 'Sales'}, + {'name': 'Bob', 'dept': 'Sales'}, + {'name': 'Charlie', 'dept': 'IT'}, +] + +for dept, people in groupby(data, key=lambda x: x['dept']): + print(f"{dept}: {list(people)}") + +# Slice iterator +first_10 = list(islice(infinite_generator(), 10)) +``` + +--- + +## Type Hints + +### Basic Type Hints + +```python +from typing import List, Dict, Optional, Union, Tuple + +# Function type hints +def calculate_total(items: List[float]) -> float: + return sum(items) + +def get_user(user_id: int) -> Optional[User]: + return User.objects.filter(id=user_id).first() + +# Variable type hints +name: str = "John" +age: int = 30 +prices: List[float] = [10.99, 20.50, 5.25] +user_data: Dict[str, Union[str, int]] = { + 'name': 'John', + 'age': 30 +} +``` + +### Advanced Type Hints + +```python +from typing import TypeVar, Generic, Protocol, Callable + +# Generic types +T = TypeVar('T') + +def first_element(items: List[T]) -> Optional[T]: + return items[0] if items else None + +# Protocol (structural subtyping) +class Drawable(Protocol): + def draw(self) -> None: + ... + +def render(obj: Drawable) -> None: + obj.draw() + +# Callable +def apply_operation( + items: List[int], + operation: Callable[[int], int] +) -> List[int]: + return [operation(item) for item in items] + +# TypedDict (Python 3.8+) +from typing import TypedDict + +class UserDict(TypedDict): + id: int + name: str + email: str + age: Optional[int] + +def create_user(data: UserDict) -> User: + return User(**data) +``` + +### Type Checking + +```python +# Use mypy for static type checking +# Install: pip install mypy +# Run: mypy your_script.py + +# Example with type errors +def greet(name: str) -> str: + return f"Hello, {name}" + +greet(123) # mypy will catch this error +``` + +--- + +## Error Handling + +### Exception Handling + +```python +# Bad - bare except +try: + result = risky_operation() +except: + print("Error occurred") + +# Good - specific exceptions +try: + result = risky_operation() +except ValueError as e: + print(f"Invalid value: {e}") +except KeyError as e: + print(f"Missing key: {e}") +except Exception as e: + print(f"Unexpected error: {e}") + raise # Re-raise if you can't handle it + +# Finally clause +try: + file = open('data.txt') + process_file(file) +except IOError as e: + print(f"File error: {e}") +finally: + file.close() # Always executed +``` + +### Custom Exceptions + +```python +# Create custom exception classes +class ValidationError(Exception): + """Raised when validation fails""" + def __init__(self, field: str, message: str): + self.field = field + self.message = message + super().__init__(f"{field}: {message}") + +class NotFoundError(Exception): + """Raised when resource is not found""" + def __init__(self, resource_type: str, resource_id: int): + self.resource_type = resource_type + self.resource_id = resource_id + super().__init__( + f"{resource_type} with id {resource_id} not found" + ) + +# Usage +def validate_user(user_data: dict) -> None: + if not user_data.get('email'): + raise ValidationError('email', 'Email is required') + +def get_user(user_id: int) -> User: + user = User.objects.filter(id=user_id).first() + if not user: + raise NotFoundError('User', user_id) + return user +``` + +--- + +## When to Load Django Guidance + +Load `python-django.md` instead of relying on this file when: + +- The code depends on Django models, QuerySets, forms, admin, middleware, or templates +- ORM query shape and N+1 risks are part of the refactor +- Class-based views, serializers, or app structure conventions matter +- The simplification touches request lifecycle, authentication, or transaction boundaries + +--- + +## Python Simplification Checklist + +- [ ] Following PEP 8 style guide +- [ ] Using f-strings for string formatting +- [ ] List/dict/set comprehensions where appropriate +- [ ] Context managers for resource management +- [ ] Generators for large datasets +- [ ] Type hints on functions and methods +- [ ] Specific exception handling (not bare except) +- [ ] Custom exception classes for domain errors +- [ ] EAFP over LBYL +- [ ] Enumerate and zip for iteration +- [ ] No nested ternary operators + +--- + +## Additional Resources + +- PEP 8 Style Guide: https://pep8.org/ +- Python Documentation: https://docs.python.org/3/ +- Real Python: https://realpython.com/ +- Python Type Checking: https://mypy.readthedocs.io/ + +**Python Version Recommendation**: Use Python 3.10+ for best +features and performance. diff --git a/code-simplifier/steering/rust.md b/code-simplifier/steering/rust.md new file mode 100644 index 0000000..2a09b05 --- /dev/null +++ b/code-simplifier/steering/rust.md @@ -0,0 +1,601 @@ +# Rust Code Simplification Guide + +This guide covers Rust-specific simplification patterns and +best practices for writing idiomatic Rust code. + +## Table of Contents + +1. [Rust Style and Conventions](#rust-style-and-conventions) +2. [Ownership and Borrowing](#ownership-and-borrowing) +3. [Error Handling](#error-handling) +4. [Pattern Matching](#pattern-matching) +5. [Iterators and Functional Patterns](#iterators-and-functional-patterns) +6. [Type System](#type-system) +7. [Traits and Generics](#traits-and-generics) +8. [Concurrency](#concurrency) + +--- + +## Rust Style and Conventions + +### Naming Conventions + +```rust +// Variables and functions - snake_case +let user_name = "John"; +fn calculate_total(items: &[Item]) -> f64 { + // implementation +} + +// Types and traits - PascalCase +struct UserService; +trait Drawable { + fn draw(&self); +} + +// Constants - UPPER_SNAKE_CASE +const MAX_RETRIES: u32 = 3; +const DEFAULT_TIMEOUT: u64 = 30; + +// Lifetimes - lowercase single letter +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + if x.len() > y.len() { x } else { y } +} +``` + +### Module Organization + +```rust +// lib.rs or main.rs +mod models; +mod services; +mod utils; + +// Re-export commonly used items +pub use models::{User, Order}; +pub use services::UserService; + +// Conditional compilation +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_something() { + // test code + } +} +``` + +--- + +## Ownership and Borrowing + +### Prefer Borrowing Over Cloning + +```rust +// Bad - unnecessary clone +fn process_data(data: Vec) -> usize { + data.len() +} + +let items = vec!["a".to_string(), "b".to_string()]; +let count = process_data(items.clone()); // Expensive clone + +// Good - borrow instead +fn process_data(data: &[String]) -> usize { + data.len() +} + +let items = vec!["a".to_string(), "b".to_string()]; +let count = process_data(&items); // No clone needed +``` + +### Use References Appropriately + +```rust +// Bad - taking ownership when not needed +fn print_user(user: User) { + println!("{}", user.name); +} // user is dropped here + +// Good - borrow for read-only access +fn print_user(user: &User) { + println!("{}", user.name); +} + +// Mutable borrow when modification is needed +fn update_user(user: &mut User, new_name: String) { + user.name = new_name; +} +``` + +### Avoid Unnecessary String Conversions + +```rust +// Bad - unnecessary allocations +fn greet(name: String) -> String { + format!("Hello, {}", name) +} + +let greeting = greet("Alice".to_string()); + +// Good - use string slices +fn greet(name: &str) -> String { + format!("Hello, {}", name) +} + +let greeting = greet("Alice"); +``` + +--- + +## Error Handling + +### Use Result and Option + +```rust +// Bad - panic on error +fn parse_number(s: &str) -> i32 { + s.parse().unwrap() // Panics on invalid input +} + +// Good - return Result +fn parse_number(s: &str) -> Result { + s.parse() +} + +// Usage with error propagation +fn process_input(input: &str) -> Result> { + let num = parse_number(input)?; // ? operator propagates error + Ok(num * 2) +} +``` + +### Custom Error Types + +```rust +use std::fmt; + +// Define custom error type +#[derive(Debug)] +enum AppError { + NotFound(String), + ValidationError { field: String, message: String }, + DatabaseError(String), +} + +impl fmt::Display for AppError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + AppError::NotFound(resource) => + write!(f, "Resource not found: {}", resource), + AppError::ValidationError { field, message } => + write!(f, "Validation error on {}: {}", field, message), + AppError::DatabaseError(msg) => + write!(f, "Database error: {}", msg), + } + } +} + +impl std::error::Error for AppError {} + +// Usage +fn get_user(id: u64) -> Result { + if id == 0 { + return Err(AppError::ValidationError { + field: "id".to_string(), + message: "ID cannot be zero".to_string(), + }); + } + // ... fetch user + Ok(user) +} +``` + +### The ? Operator + +```rust +// Bad - nested match statements +fn read_and_parse(path: &str) -> Result> { + match std::fs::read_to_string(path) { + Ok(contents) => { + match contents.trim().parse() { + Ok(num) => Ok(num), + Err(e) => Err(Box::new(e)), + } + } + Err(e) => Err(Box::new(e)), + } +} + +// Good - use ? operator +fn read_and_parse(path: &str) -> Result> { + let contents = std::fs::read_to_string(path)?; + let num = contents.trim().parse()?; + Ok(num) +} +``` + +--- + +## Pattern Matching + +### Exhaustive Matching + +```rust +enum Status { + Pending, + Processing, + Completed, + Failed(String), +} + +// Bad - using if-else chains +fn handle_status(status: Status) { + if matches!(status, Status::Pending) { + println!("Pending"); + } else if matches!(status, Status::Processing) { + println!("Processing"); + } + // Missing cases! +} + +// Good - exhaustive match +fn handle_status(status: Status) { + match status { + Status::Pending => println!("Pending"), + Status::Processing => println!("Processing"), + Status::Completed => println!("Completed"), + Status::Failed(reason) => println!("Failed: {}", reason), + } +} +``` + +### Match Guards and Patterns + +```rust +// Use match guards for complex conditions +fn categorize_number(n: i32) -> &'static str { + match n { + n if n < 0 => "negative", + 0 => "zero", + n if n % 2 == 0 => "positive even", + _ => "positive odd", + } +} + +// Destructuring in patterns +struct Point { x: i32, y: i32 } + +fn describe_point(point: Point) -> String { + match point { + Point { x: 0, y: 0 } => "origin".to_string(), + Point { x: 0, y } => format!("on y-axis at {}", y), + Point { x, y: 0 } => format!("on x-axis at {}", x), + Point { x, y } => format!("at ({}, {})", x, y), + } +} +``` + +### if let and while let + +```rust +// Bad - match with single arm +match some_option { + Some(value) => println!("Got: {}", value), + None => {} +} + +// Good - if let for single pattern +if let Some(value) = some_option { + println!("Got: {}", value); +} + +// while let for iterating until None +let mut stack = vec![1, 2, 3]; +while let Some(top) = stack.pop() { + println!("{}", top); +} +``` + +--- + +## Iterators and Functional Patterns + +### Prefer Iterators Over Loops + +```rust +// Bad - manual loop +let mut sum = 0; +for i in 0..10 { + sum += i * i; +} + +// Good - iterator chain +let sum: i32 = (0..10).map(|i| i * i).sum(); + +// Filter and map +let even_squares: Vec = (0..10) + .filter(|&x| x % 2 == 0) + .map(|x| x * x) + .collect(); +``` + +### Iterator Adapters + +```rust +let numbers = vec![1, 2, 3, 4, 5]; + +// take, skip, enumerate +let first_three: Vec<_> = numbers.iter().take(3).collect(); +let skip_two: Vec<_> = numbers.iter().skip(2).collect(); + +for (index, value) in numbers.iter().enumerate() { + println!("{}: {}", index, value); +} + +// find, any, all +let has_even = numbers.iter().any(|&x| x % 2 == 0); +let all_positive = numbers.iter().all(|&x| x > 0); +let first_even = numbers.iter().find(|&&x| x % 2 == 0); + +// fold for custom accumulation +let product = numbers.iter().fold(1, |acc, &x| acc * x); +``` + +### Avoid Collecting Unnecessarily + +```rust +// Bad - unnecessary collect +let sum: i32 = numbers.iter() + .map(|&x| x * 2) + .collect::>() + .iter() + .sum(); + +// Good - chain iterators +let sum: i32 = numbers.iter() + .map(|&x| x * 2) + .sum(); +``` + +--- + +## Type System + +### Type Inference + +```rust +// Bad - unnecessary type annotations +let x: i32 = 5; +let y: f64 = 3.14; +let name: String = String::from("Alice"); + +// Good - let compiler infer +let x = 5; +let y = 3.14; +let name = String::from("Alice"); + +// Annotate when needed for clarity or disambiguation +let numbers: Vec = vec![]; // Empty vec needs type +let result: Result<_, AppError> = parse_data(); // Specify error type +``` + +### Newtype Pattern + +```rust +// Bad - primitive obsession +fn transfer_money(from: u64, to: u64, amount: f64) { + // Easy to mix up parameters +} + +// Good - newtype pattern +struct UserId(u64); +struct Amount(f64); + +fn transfer_money(from: UserId, to: UserId, amount: Amount) { + // Type safety prevents mistakes +} +``` + +### Type Aliases + +```rust +// Simplify complex types +type Result = std::result::Result; +type UserId = u64; +type UserMap = std::collections::HashMap; + +// Usage +fn get_user(id: UserId) -> Result { + // implementation +} +``` + +--- + +## Traits and Generics + +### Implement Standard Traits + +```rust +#[derive(Debug, Clone, PartialEq, Eq)] +struct User { + id: u64, + name: String, + email: String, +} + +// Implement Display for user-friendly output +impl std::fmt::Display for User { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{} ({})", self.name, self.email) + } +} + +// Implement From for conversions +impl From<(u64, String, String)> for User { + fn from((id, name, email): (u64, String, String)) -> Self { + User { id, name, email } + } +} +``` + +### Generic Functions and Trait Bounds + +```rust +// Bad - concrete types +fn print_vec_i32(v: &Vec) { + for item in v { + println!("{}", item); + } +} + +// Good - generic with trait bounds +fn print_vec(v: &[T]) { + for item in v { + println!("{}", item); + } +} + +// Multiple trait bounds +fn process(item: T) -> String +where + T: std::fmt::Display + std::fmt::Debug + Clone, +{ + format!("{} - {:?}", item, item.clone()) +} +``` + +### Trait Objects vs Generics + +```rust +// Static dispatch (generics) - faster, larger binary +fn process_static(processor: T, data: &str) { + processor.process(data); +} + +// Dynamic dispatch (trait objects) - smaller binary, runtime cost +fn process_dynamic(processor: &dyn Processor, data: &str) { + processor.process(data); +} + +// Use generics when performance matters +// Use trait objects when you need heterogeneous collections +let processors: Vec> = vec![ + Box::new(JsonProcessor), + Box::new(XmlProcessor), +]; +``` + +--- + +## Concurrency + +### Use Channels for Communication + +```rust +use std::sync::mpsc; +use std::thread; + +// Create channel +let (tx, rx) = mpsc::channel(); + +// Spawn thread +thread::spawn(move || { + let result = expensive_computation(); + tx.send(result).unwrap(); +}); + +// Receive result +let result = rx.recv().unwrap(); +``` + +### Shared State with Arc and Mutex + +```rust +use std::sync::{Arc, Mutex}; +use std::thread; + +// Bad - trying to share mutable state +let counter = 0; +let handle = thread::spawn(|| { + counter += 1; // Error: can't capture mutable reference +}); + +// Good - Arc> for shared mutable state +let counter = Arc::new(Mutex::new(0)); +let counter_clone = Arc::clone(&counter); + +let handle = thread::spawn(move || { + let mut num = counter_clone.lock().unwrap(); + *num += 1; +}); + +handle.join().unwrap(); +println!("Result: {}", *counter.lock().unwrap()); +``` + +### Async/Await + +```rust +use tokio; + +// Async function +async fn fetch_data(url: &str) -> Result { + let response = reqwest::get(url).await?; + let body = response.text().await?; + Ok(body) +} + +// Concurrent execution +#[tokio::main] +async fn main() { + let urls = vec![ + "https://api.example.com/1", + "https://api.example.com/2", + "https://api.example.com/3", + ]; + + // Execute concurrently + let futures: Vec<_> = urls.iter() + .map(|url| fetch_data(url)) + .collect(); + + let results = futures::future::join_all(futures).await; +} +``` + +--- + +## Rust Simplification Checklist + +- [ ] Following Rust naming conventions (snake_case, + PascalCase) +- [ ] Prefer borrowing over cloning +- [ ] Use Result and Option instead of panicking +- [ ] Exhaustive pattern matching +- [ ] Iterator chains over manual loops +- [ ] Derive standard traits (Debug, Clone, etc.) +- [ ] Use ? operator for error propagation +- [ ] Appropriate use of lifetimes +- [ ] Generic functions with trait bounds +- [ ] Arc> for shared mutable state +- [ ] Async/await for concurrent operations +- [ ] No nested ternary operators (use match instead) + +--- + +## Additional Resources + +- The Rust Book: https://doc.rust-lang.org/book/ +- Rust by Example: + https://doc.rust-lang.org/rust-by-example/ +- Rust API Guidelines: + https://rust-lang.github.io/api-guidelines/ +- Clippy Lints: https://rust-lang.github.io/rust-clippy/ +- Tokio Documentation: https://tokio.rs/ + +**Rust Version Recommendation**: Use the latest stable +version for best features and tooling. diff --git a/code-simplifier/steering/typescript.md b/code-simplifier/steering/typescript.md new file mode 100644 index 0000000..6e874ef --- /dev/null +++ b/code-simplifier/steering/typescript.md @@ -0,0 +1,873 @@ +# TypeScript Code Simplification Guide + +This guide covers TypeScript-specific simplification +patterns and best practices for writing type-safe, +maintainable code. + +## Table of Contents + +1. [TypeScript Style and Conventions](#typescript-style-and-conventions) +2. [Type System](#type-system) +3. [Interfaces vs Types](#interfaces-vs-types) +4. [Generics](#generics) +5. [Utility Types](#utility-types) +6. [Async/Await and Promises](#asyncawait-and-promises) +7. [Modern JavaScript Features](#modern-javascript-features) +8. [React with TypeScript](#react-with-typescript) + +--- + +## TypeScript Style and Conventions + +### Naming Conventions + +```typescript +// Interfaces and Types - PascalCase +interface User { + id: number; + name: string; +} + +type UserRole = "admin" | "user" | "guest"; + +// Classes - PascalCase +class UserService { + // Private fields - camelCase with # or private keyword + #repository: UserRepository; + + // Public properties - camelCase + userName: string; + + // Methods - camelCase + async getUser(id: number): Promise { + // implementation + } +} + +// Functions and variables - camelCase +const calculateTotal = (items: Item[]): number => { + return items.reduce((sum, item) => sum + item.price, 0); +}; + +// Constants - UPPER_SNAKE_CASE or camelCase +const MAX_RETRIES = 3; +const apiBaseUrl = "https://api.example.com"; + +// Enums - PascalCase for enum, UPPER_SNAKE_CASE for values +enum OrderStatus { + PENDING = "PENDING", + PROCESSING = "PROCESSING", + COMPLETED = "COMPLETED" +} +``` + +### File Organization + +```typescript +// user.types.ts - Type definitions +export interface User { + id: number; + name: string; + email: string; +} + +export type UserRole = "admin" | "user" | "guest"; + +// user.service.ts - Implementation +import { User, UserRole } from "./user.types"; + +export class UserService { + async getUser(id: number): Promise { + // implementation + } +} + +// index.ts - Barrel export +export * from "./user.types"; +export * from "./user.service"; +``` + +--- + +## Type System + +### Type Inference + +```typescript +// Bad - unnecessary type annotations +const name: string = "John"; +const age: number = 30; +const isActive: boolean = true; + +// Good - let TypeScript infer +const name = "John"; +const age = 30; +const isActive = true; + +// Annotate when needed +const users: User[] = []; // Empty array needs type +const result: Promise = fetchUser(); // Clarify return type +``` + +### Avoid `any` + +```typescript +// Bad - loses type safety +function processData(data: any): any { + return data.value; +} + +// Good - use specific types +function processData(data: { value: string }): string { + return data.value; +} + +// Better - use generics for flexibility +function processData( + data: T +): string { + return data.value; +} + +// Use unknown for truly unknown types +function processData(data: unknown): string { + if ( + typeof data === "object" && + data !== null && + "value" in data + ) { + return String((data as { value: unknown }).value); + } + throw new Error("Invalid data"); +} +``` + +### Union and Intersection Types + +```typescript +// Union types - value can be one of several types +type Status = "pending" | "success" | "error"; +type Result = User | Error; + +function handleResult(result: Result): void { + if (result instanceof Error) { + console.error(result.message); + } else { + console.log(result.name); + } +} + +// Intersection types - combine multiple types +type Timestamped = { + createdAt: Date; + updatedAt: Date; +}; + +type User = { + id: number; + name: string; +}; + +type TimestampedUser = User & Timestamped; + +const user: TimestampedUser = { + id: 1, + name: "John", + createdAt: new Date(), + updatedAt: new Date() +}; +``` + +### Type Guards + +```typescript +// Type predicate +function isUser(obj: unknown): obj is User { + return ( + typeof obj === "object" && + obj !== null && + "id" in obj && + "name" in obj + ); +} + +// Usage +function processData(data: unknown): void { + if (isUser(data)) { + console.log(data.name); // TypeScript knows it's User + } +} + +// Discriminated unions +type Success = { status: "success"; data: User }; +type Error = { status: "error"; message: string }; +type Result = Success | Error; + +function handleResult(result: Result): void { + if (result.status === "success") { + console.log(result.data); // TypeScript knows it's Success + } else { + console.log(result.message); // TypeScript knows it's Error + } +} +``` + +--- + +## Interfaces vs Types + +### When to Use Each + +```typescript +// Use interface for object shapes (can be extended) +interface User { + id: number; + name: string; +} + +// Extend interface +interface AdminUser extends User { + permissions: string[]; +} + +// Use type for unions, intersections, primitives +type Status = "pending" | "success" | "error"; +type ID = string | number; +type Point = { x: number; y: number }; + +// Type can do everything interface can (with &) +type User = { + id: number; + name: string; +}; + +type AdminUser = User & { + permissions: string[]; +}; +``` + +### Declaration Merging (Interface Only) + +```typescript +// Interfaces can be merged +interface User { + id: number; + name: string; +} + +interface User { + email: string; +} + +// Merged result +const user: User = { + id: 1, + name: "John", + email: "john@example.com" +}; + +// Types cannot be merged +type User = { id: number }; // Error if declared again +``` + +### Recommendation + +```typescript +// Use interface for public APIs and object shapes +export interface User { + id: number; + name: string; +} + +// Use type for complex types, unions, and internal types +type UserRole = "admin" | "user" | "guest"; +type Result = + | { success: true; data: T } + | { success: false; error: string }; +``` + +--- + +## Generics + +### Generic Functions + +```typescript +// Bad - duplicated code +function getFirstString(arr: string[]): string | undefined { + return arr[0]; +} + +function getFirstNumber(arr: number[]): number | undefined { + return arr[0]; +} + +// Good - generic function +function getFirst(arr: T[]): T | undefined { + return arr[0]; +} + +// Usage +const firstString = getFirst(["a", "b", "c"]); // string | undefined +const firstNumber = getFirst([1, 2, 3]); // number | undefined +``` + +### Generic Constraints + +```typescript +// Constrain generic to have certain properties +function getProperty( + obj: T, + key: K +): T[K] { + return obj[key]; +} + +const user = { + id: 1, + name: "John", + email: "john@example.com" +}; +const name = getProperty(user, "name"); // string +const id = getProperty(user, "id"); // number + +// Constrain to have specific shape +interface HasId { + id: number; +} + +function findById( + items: T[], + id: number +): T | undefined { + return items.find((item) => item.id === id); +} +``` + +### Generic Classes + +```typescript +class DataStore { + private data: T[] = []; + + add(item: T): void { + this.data.push(item); + } + + get(index: number): T | undefined { + return this.data[index]; + } + + filter(predicate: (item: T) => boolean): T[] { + return this.data.filter(predicate); + } +} + +// Usage +const userStore = new DataStore(); +userStore.add({ id: 1, name: "John" }); +const user = userStore.get(0); // User | undefined +``` + +--- + +## Utility Types + +### Built-in Utility Types + +```typescript +interface User { + id: number; + name: string; + email: string; + age: number; +} + +// Partial - all properties optional +type PartialUser = Partial; +const update: PartialUser = { name: "John" }; + +// Required - all properties required +type RequiredUser = Required>; + +// Readonly - all properties readonly +type ReadonlyUser = Readonly; + +// Pick - select specific properties +type UserPreview = Pick; +const preview: UserPreview = { id: 1, name: "John" }; + +// Omit - exclude specific properties +type UserWithoutEmail = Omit; + +// Record - create object type with specific keys +type UserRoles = Record; +const roles: UserRoles = { + john: "admin", + jane: "user" +}; + +// ReturnType - extract return type of function +function getUser(): User { + return { + id: 1, + name: "John", + email: "john@example.com", + age: 30 + }; +} +type UserType = ReturnType; // User + +// Parameters - extract parameter types +function createUser(name: string, age: number): User { + return { id: 1, name, email: "", age }; +} +type CreateUserParams = Parameters; // [string, number] +``` + +### Custom Utility Types + +```typescript +// Make specific properties optional +type PartialBy = Omit & + Partial>; + +type UserWithOptionalEmail = PartialBy; + +// Make specific properties required +type RequiredBy = Omit & + Required>; + +// Deep partial +type DeepPartial = { + [P in keyof T]?: T[P] extends object + ? DeepPartial + : T[P]; +}; + +// Non-nullable +type NonNullableFields = { + [P in keyof T]: NonNullable; +}; +``` + +--- + +## Async/Await and Promises + +### Async Functions + +```typescript +// Bad - callback hell +function getUser( + id: number, + callback: (user: User) => void +): void { + fetchUser(id, (user) => { + fetchOrders(user.id, (orders) => { + callback({ ...user, orders }); + }); + }); +} + +// Good - async/await +async function getUser(id: number): Promise { + const user = await fetchUser(id); + const orders = await fetchOrders(user.id); + return { ...user, orders }; +} +``` + +### Error Handling + +```typescript +// Bad - unhandled promise rejection +async function getUser(id: number): Promise { + const response = await fetch(`/api/users/${id}`); + return response.json(); +} + +// Good - proper error handling +async function getUser(id: number): Promise { + try { + const response = await fetch(`/api/users/${id}`); + + if (!response.ok) { + throw new Error( + `HTTP error! status: ${response.status}` + ); + } + + return await response.json(); + } catch (error) { + console.error("Failed to fetch user:", error); + throw error; + } +} + +// Better - typed errors +class ApiError extends Error { + constructor( + public status: number, + message: string + ) { + super(message); + this.name = "ApiError"; + } +} + +async function getUser(id: number): Promise { + const response = await fetch(`/api/users/${id}`); + + if (!response.ok) { + throw new ApiError( + response.status, + `Failed to fetch user ${id}` + ); + } + + return response.json(); +} +``` + +### Parallel Execution + +```typescript +// Bad - sequential +async function loadData( + userId: number +): Promise { + const user = await fetchUser(userId); + const orders = await fetchOrders(userId); + const preferences = await fetchPreferences(userId); + + return { user, orders, preferences }; +} + +// Good - parallel +async function loadData( + userId: number +): Promise { + const [user, orders, preferences] = await Promise.all([ + fetchUser(userId), + fetchOrders(userId), + fetchPreferences(userId) + ]); + + return { user, orders, preferences }; +} + +// With error handling for individual promises +async function loadData( + userId: number +): Promise { + const results = await Promise.allSettled([ + fetchUser(userId), + fetchOrders(userId), + fetchPreferences(userId) + ]); + + const [userResult, ordersResult, preferencesResult] = + results; + + return { + user: + userResult.status === "fulfilled" + ? userResult.value + : null, + orders: + ordersResult.status === "fulfilled" + ? ordersResult.value + : [], + preferences: + preferencesResult.status === "fulfilled" + ? preferencesResult.value + : {} + }; +} +``` + +--- + +## Modern JavaScript Features + +### Destructuring + +```typescript +// Object destructuring +const user = { + id: 1, + name: "John", + email: "john@example.com" +}; + +// Bad +const id = user.id; +const name = user.name; + +// Good +const { id, name } = user; + +// With renaming +const { id: userId, name: userName } = user; + +// With defaults +const { id, name, age = 0 } = user; + +// Array destructuring +const numbers = [1, 2, 3, 4, 5]; +const [first, second, ...rest] = numbers; + +// Function parameters +function printUser({ id, name }: User): void { + console.log(`${id}: ${name}`); +} +``` + +### Spread and Rest + +```typescript +// Spread operator +const user = { id: 1, name: "John" }; +const updatedUser = { ...user, email: "john@example.com" }; + +const numbers = [1, 2, 3]; +const moreNumbers = [...numbers, 4, 5, 6]; + +// Rest parameters +function sum(...numbers: number[]): number { + return numbers.reduce((total, n) => total + n, 0); +} + +sum(1, 2, 3, 4, 5); // 15 +``` + +### Optional Chaining and Nullish Coalescing + +```typescript +// Optional chaining (?.) +const userName = user?.profile?.name; +const firstOrder = user?.orders?.[0]; +const result = user?.getOrders?.(); + +// Nullish coalescing (??) +const name = user.name ?? "Unknown"; +const port = config.port ?? 3000; + +// Different from || +const count = 0; +const value1 = count || 10; // 10 (0 is falsy) +const value2 = count ?? 10; // 0 (0 is not null/undefined) +``` + +### Template Literals + +```typescript +// Bad - string concatenation +const message = + "Hello, " + + user.name + + "! You have " + + user.orderCount + + " orders."; + +// Good - template literal +const message = `Hello, ${user.name}! You have ${user.orderCount} orders.`; + +// Multi-line strings +const html = ` +
    +

    ${user.name}

    +

    ${user.email}

    +
    +`; + +// Tagged templates +function sql( + strings: TemplateStringsArray, + ...values: unknown[] +): string { + // Custom processing + return strings.reduce((query, str, i) => { + return ( + query + + str + + (values[i] !== undefined + ? `'${values[i]}'` + : "") + ); + }, ""); +} + +const query = sql`SELECT * FROM users WHERE id = ${userId}`; +``` + +--- + +## React with TypeScript + +### Component Props + +```typescript +// Bad - no types +function Button(props) { + return ; +} + +// Good - typed props +interface ButtonProps { + label: string; + onClick: () => void; + disabled?: boolean; +} + +function Button({ label, onClick, disabled = false }: ButtonProps) { + return ( + + ); +} + +// With children +interface CardProps { + title: string; + children: React.ReactNode; +} + +function Card({ title, children }: CardProps) { + return ( +
    +

    {title}

    + {children} +
    + ); +} +``` + +### Hooks with TypeScript + +```typescript +// useState with type inference +const [count, setCount] = useState(0); // number +const [name, setName] = useState(""); // string + +// useState with explicit type +const [user, setUser] = useState(null); + +// useRef +const inputRef = useRef(null); + +// useEffect +useEffect(() => { + // Effect logic + + return () => { + // Cleanup + }; +}, [dependency]); + +// Custom hook +function useUser(userId: number) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + + async function fetchUser() { + try { + const data = await getUser(userId); + if (!cancelled) { + setUser(data); + } + } catch (err) { + if (!cancelled) { + setError(err as Error); + } + } finally { + if (!cancelled) { + setLoading(false); + } + } + } + + fetchUser(); + + return () => { + cancelled = true; + }; + }, [userId]); + + return { user, loading, error }; +} +``` + +### Event Handlers + +```typescript +// Form events +function LoginForm() { + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + // Handle form submission + }; + + const handleChange = (event: React.ChangeEvent) => { + console.log(event.target.value); + }; + + return ( +
    + + +
    + ); +} + +// Mouse events +function Button() { + const handleClick = (event: React.MouseEvent) => { + console.log('Clicked at', event.clientX, event.clientY); + }; + + return ; +} +``` + +--- + +## TypeScript Simplification Checklist + +- [ ] Following TypeScript naming conventions +- [ ] Let TypeScript infer types when obvious +- [ ] Avoid `any`, use `unknown` for truly unknown types +- [ ] Use union and intersection types appropriately +- [ ] Type guards for runtime type checking +- [ ] Prefer interface for object shapes, type for unions +- [ ] Generic functions and classes for reusability +- [ ] Built-in utility types (Partial, Pick, Omit, etc.) +- [ ] Async/await for asynchronous operations +- [ ] Proper error handling in async functions +- [ ] Destructuring for cleaner code +- [ ] Optional chaining and nullish coalescing +- [ ] Template literals for string interpolation +- [ ] Typed React components and hooks +- [ ] No nested ternary operators (use switch or if-else) + +--- + +## Additional Resources + +- TypeScript Handbook: + https://www.typescriptlang.org/docs/handbook/ +- TypeScript Deep Dive: + https://basarat.gitbook.io/typescript/ +- React TypeScript Cheatsheet: + https://react-typescript-cheatsheet.netlify.app/ +- Type Challenges: + https://github.com/type-challenges/type-challenges + +**TypeScript Version Recommendation**: Use TypeScript 5.0+ +for latest features and performance improvements.