feat: add Conductor plugin for Context-Driven Development

Add comprehensive Conductor plugin implementing Context-Driven Development
methodology with tracks, specs, and phased implementation plans.

Components:
- 5 commands: setup, new-track, implement, status, revert
- 1 agent: conductor-validator
- 3 skills: context-driven-development, track-management, workflow-patterns
- 18 templates for project artifacts

Documentation updates:
- README.md: Updated counts (68 plugins, 100 agents, 110 skills, 76 tools)
- docs/plugins.md: Added Conductor to Workflows section
- docs/agents.md: Added conductor-validator agent
- docs/agent-skills.md: Added Conductor skills section

Also includes Prettier formatting across all project files.
This commit is contained in:
Seth Hobson
2026-01-15 17:38:21 -05:00
parent 87231b828d
commit f662524f9a
94 changed files with 11610 additions and 1728 deletions

View File

@@ -0,0 +1,600 @@
# C# Style Guide
C# conventions and best practices for .NET development.
## Naming Conventions
### General Rules
```csharp
// PascalCase for public members, types, namespaces
public class UserService { }
public void ProcessOrder() { }
public string FirstName { get; set; }
// camelCase for private fields, parameters, locals
private readonly ILogger _logger;
private int _itemCount;
public void DoWork(string inputValue) { }
// Prefix interfaces with I
public interface IUserRepository { }
public interface INotificationService { }
// Suffix async methods with Async
public async Task<User> GetUserAsync(int id) { }
public async Task ProcessOrderAsync(Order order) { }
// Constants: PascalCase (not SCREAMING_CASE)
public const int MaxRetryCount = 3;
public const string DefaultCurrency = "USD";
```
### Field and Property Naming
```csharp
public class Order
{
// Private fields: underscore prefix + camelCase
private readonly IOrderRepository _repository;
private int _itemCount;
// Public properties: PascalCase
public int Id { get; set; }
public string CustomerName { get; set; }
public DateTime CreatedAt { get; init; }
// Boolean properties: Is/Has/Can prefix
public bool IsActive { get; set; }
public bool HasDiscount { get; set; }
public bool CanEdit { get; }
}
```
## Async/Await Patterns
### Basic Async Usage
```csharp
// Always use async/await for I/O operations
public async Task<User> GetUserAsync(int id)
{
var user = await _repository.FindAsync(id);
if (user == null)
{
throw new NotFoundException($"User {id} not found");
}
return user;
}
// Don't block on async code
// Bad
var user = GetUserAsync(id).Result;
// Good
var user = await GetUserAsync(id);
```
### Async Best Practices
```csharp
// Use ConfigureAwait(false) in library code
public async Task<Data> FetchDataAsync()
{
var response = await _httpClient.GetAsync(url)
.ConfigureAwait(false);
return await response.Content.ReadAsAsync<Data>()
.ConfigureAwait(false);
}
// Avoid async void except for event handlers
// Bad
public async void ProcessOrder() { }
// Good
public async Task ProcessOrderAsync() { }
// Event handler exception
private async void Button_Click(object sender, EventArgs e)
{
try
{
await ProcessOrderAsync();
}
catch (Exception ex)
{
HandleError(ex);
}
}
```
### Parallel Async Operations
```csharp
// Execute independent operations in parallel
public async Task<DashboardData> LoadDashboardAsync()
{
var usersTask = _userService.GetActiveUsersAsync();
var ordersTask = _orderService.GetRecentOrdersAsync();
var statsTask = _statsService.GetDailyStatsAsync();
await Task.WhenAll(usersTask, ordersTask, statsTask);
return new DashboardData
{
Users = await usersTask,
Orders = await ordersTask,
Stats = await statsTask
};
}
// Use SemaphoreSlim for throttling
public async Task ProcessItemsAsync(IEnumerable<Item> items)
{
using var semaphore = new SemaphoreSlim(10); // Max 10 concurrent
var tasks = items.Select(async item =>
{
await semaphore.WaitAsync();
try
{
await ProcessItemAsync(item);
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks);
}
```
## LINQ
### Query Syntax vs Method Syntax
```csharp
// Method syntax (preferred for simple queries)
var activeUsers = users
.Where(u => u.IsActive)
.OrderBy(u => u.Name)
.ToList();
// Query syntax (for complex queries with joins)
var orderSummary =
from order in orders
join customer in customers on order.CustomerId equals customer.Id
where order.Total > 100
group order by customer.Name into g
select new { Customer = g.Key, Total = g.Sum(o => o.Total) };
```
### LINQ Best Practices
```csharp
// Use appropriate methods
var hasItems = items.Any(); // Not: items.Count() > 0
var firstOrDefault = items.FirstOrDefault(); // Not: items.First()
var count = items.Count; // Property, not Count()
// Avoid multiple enumerations
// Bad
if (items.Any())
{
foreach (var item in items) { }
}
// Good
var itemList = items.ToList();
if (itemList.Count > 0)
{
foreach (var item in itemList) { }
}
// Project early to reduce memory
var names = users
.Where(u => u.IsActive)
.Select(u => u.Name) // Select only what you need
.ToList();
```
### Common LINQ Operations
```csharp
// Filtering
var adults = people.Where(p => p.Age >= 18);
// Transformation
var names = people.Select(p => $"{p.FirstName} {p.LastName}");
// Aggregation
var total = orders.Sum(o => o.Amount);
var average = scores.Average();
var max = values.Max();
// Grouping
var byDepartment = employees
.GroupBy(e => e.Department)
.Select(g => new { Department = g.Key, Count = g.Count() });
// Joining
var result = orders
.Join(customers,
o => o.CustomerId,
c => c.Id,
(o, c) => new { Order = o, Customer = c });
// Flattening
var allOrders = customers.SelectMany(c => c.Orders);
```
## Dependency Injection
### Service Registration
```csharp
// In Program.cs or Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Transient: new instance each time
services.AddTransient<IEmailService, EmailService>();
// Scoped: one instance per request
services.AddScoped<IUserRepository, UserRepository>();
// Singleton: one instance for app lifetime
services.AddSingleton<ICacheService, MemoryCacheService>();
// Factory registration
services.AddScoped<IDbConnection>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
return new SqlConnection(config.GetConnectionString("Default"));
});
}
```
### Constructor Injection
```csharp
public class OrderService : IOrderService
{
private readonly IOrderRepository _repository;
private readonly ILogger<OrderService> _logger;
private readonly IEmailService _emailService;
public OrderService(
IOrderRepository repository,
ILogger<OrderService> logger,
IEmailService emailService)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_emailService = emailService ?? throw new ArgumentNullException(nameof(emailService));
}
public async Task<Order> CreateOrderAsync(OrderRequest request)
{
_logger.LogInformation("Creating order for customer {CustomerId}", request.CustomerId);
var order = new Order(request);
await _repository.SaveAsync(order);
await _emailService.SendOrderConfirmationAsync(order);
return order;
}
}
```
### Options Pattern
```csharp
// Configuration class
public class EmailSettings
{
public string SmtpServer { get; set; }
public int Port { get; set; }
public string FromAddress { get; set; }
}
// Registration
services.Configure<EmailSettings>(
configuration.GetSection("Email"));
// Usage
public class EmailService
{
private readonly EmailSettings _settings;
public EmailService(IOptions<EmailSettings> options)
{
_settings = options.Value;
}
}
```
## Testing
### xUnit Basics
```csharp
public class CalculatorTests
{
[Fact]
public void Add_TwoPositiveNumbers_ReturnsSum()
{
// Arrange
var calculator = new Calculator();
// Act
var result = calculator.Add(2, 3);
// Assert
Assert.Equal(5, result);
}
[Theory]
[InlineData(1, 1, 2)]
[InlineData(0, 0, 0)]
[InlineData(-1, 1, 0)]
public void Add_VariousNumbers_ReturnsCorrectSum(int a, int b, int expected)
{
var calculator = new Calculator();
Assert.Equal(expected, calculator.Add(a, b));
}
}
```
### Mocking with Moq
```csharp
public class OrderServiceTests
{
private readonly Mock<IOrderRepository> _mockRepository;
private readonly Mock<ILogger<OrderService>> _mockLogger;
private readonly OrderService _service;
public OrderServiceTests()
{
_mockRepository = new Mock<IOrderRepository>();
_mockLogger = new Mock<ILogger<OrderService>>();
_service = new OrderService(_mockRepository.Object, _mockLogger.Object);
}
[Fact]
public async Task GetOrderAsync_ExistingOrder_ReturnsOrder()
{
// Arrange
var expectedOrder = new Order { Id = 1, Total = 100m };
_mockRepository
.Setup(r => r.FindAsync(1))
.ReturnsAsync(expectedOrder);
// Act
var result = await _service.GetOrderAsync(1);
// Assert
Assert.Equal(expectedOrder.Id, result.Id);
_mockRepository.Verify(r => r.FindAsync(1), Times.Once);
}
[Fact]
public async Task GetOrderAsync_NonExistingOrder_ThrowsNotFoundException()
{
// Arrange
_mockRepository
.Setup(r => r.FindAsync(999))
.ReturnsAsync((Order)null);
// Act & Assert
await Assert.ThrowsAsync<NotFoundException>(
() => _service.GetOrderAsync(999));
}
}
```
### Integration Testing
```csharp
public class ApiIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public ApiIntegrationTests(WebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task GetUsers_ReturnsSuccessAndCorrectContentType()
{
// Act
var response = await _client.GetAsync("/api/users");
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("application/json; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
```
## Common Patterns
### Null Handling
```csharp
// Null-conditional operators
var length = customer?.Address?.Street?.Length;
var name = user?.Name ?? "Unknown";
// Null-coalescing assignment
list ??= new List<Item>();
// Pattern matching for null checks
if (user is not null)
{
ProcessUser(user);
}
// Guard clauses
public void ProcessOrder(Order order)
{
ArgumentNullException.ThrowIfNull(order);
if (order.Items.Count == 0)
{
throw new ArgumentException("Order must have items", nameof(order));
}
// Process...
}
```
### Records and Init-Only Properties
```csharp
// Record for immutable data
public record User(int Id, string Name, string Email);
// Record with additional members
public record Order
{
public int Id { get; init; }
public string CustomerName { get; init; }
public decimal Total { get; init; }
public bool IsHighValue => Total > 1000;
}
// Record mutation via with expression
var updatedUser = user with { Name = "New Name" };
```
### Pattern Matching
```csharp
// Type patterns
public decimal CalculateDiscount(object customer) => customer switch
{
PremiumCustomer p => p.PurchaseTotal * 0.2m,
RegularCustomer r when r.YearsActive > 5 => r.PurchaseTotal * 0.1m,
RegularCustomer r => r.PurchaseTotal * 0.05m,
null => 0m,
_ => throw new ArgumentException("Unknown customer type")
};
// Property patterns
public string GetShippingOption(Order order) => order switch
{
{ Total: > 100, IsPriority: true } => "Express",
{ Total: > 100 } => "Standard",
{ IsPriority: true } => "Priority",
_ => "Economy"
};
// List patterns (C# 11)
public bool IsValidSequence(int[] numbers) => numbers switch
{
[1, 2, 3] => true,
[1, .., 3] => true,
[_, _, ..] => numbers.Length >= 2,
_ => false
};
```
### Disposable Pattern
```csharp
public class ResourceManager : IDisposable
{
private bool _disposed;
private readonly FileStream _stream;
public ResourceManager(string path)
{
_stream = File.OpenRead(path);
}
public void DoWork()
{
ObjectDisposedException.ThrowIf(_disposed, this);
// Work with _stream
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
_stream?.Dispose();
}
_disposed = true;
}
}
// Using statement
using var manager = new ResourceManager("file.txt");
manager.DoWork();
```
## Code Organization
### File Structure
```csharp
// One type per file (generally)
// Filename matches type name: UserService.cs
// Order of members
public class UserService
{
// 1. Constants
private const int MaxRetries = 3;
// 2. Static fields
private static readonly object _lock = new();
// 3. Instance fields
private readonly IUserRepository _repository;
// 4. Constructors
public UserService(IUserRepository repository)
{
_repository = repository;
}
// 5. Properties
public int TotalUsers { get; private set; }
// 6. Public methods
public async Task<User> GetUserAsync(int id) { }
// 7. Private methods
private void ValidateUser(User user) { }
}
```
### Project Structure
```
Solution/
├── src/
│ ├── MyApp.Api/ # Web API project
│ ├── MyApp.Core/ # Domain/business logic
│ ├── MyApp.Infrastructure/ # Data access, external services
│ └── MyApp.Shared/ # Shared utilities
├── tests/
│ ├── MyApp.UnitTests/
│ └── MyApp.IntegrationTests/
└── MyApp.sln
```

View File

@@ -0,0 +1,668 @@
# Dart/Flutter Style Guide
Dart language conventions and Flutter-specific patterns.
## Null Safety
### Enable Sound Null Safety
```dart
// pubspec.yaml
environment:
sdk: '>=3.0.0 <4.0.0'
// All types are non-nullable by default
String name = 'John'; // Cannot be null
String? nickname; // Can be null
// Late initialization
late final Database database;
```
### Null-Aware Operators
```dart
// Null-aware access
final length = user?.name?.length;
// Null-aware assignment
nickname ??= 'Anonymous';
// Null assertion (use sparingly)
final definitelyNotNull = maybeNull!;
// Null-aware cascade
user
?..name = 'John'
..email = 'john@example.com';
// Null coalescing
final displayName = user.nickname ?? user.name ?? 'Unknown';
```
### Null Handling Patterns
```dart
// Guard clause with null check
void processUser(User? user) {
if (user == null) {
throw ArgumentError('User cannot be null');
}
// user is promoted to non-nullable here
print(user.name);
}
// Pattern matching (Dart 3)
void handleResult(Result? result) {
switch (result) {
case Success(data: final data):
handleSuccess(data);
case Error(message: final message):
handleError(message);
case null:
handleNull();
}
}
```
## Async/Await
### Future Basics
```dart
// Async function
Future<User> fetchUser(int id) async {
final response = await http.get(Uri.parse('/users/$id'));
if (response.statusCode != 200) {
throw HttpException('Failed to fetch user');
}
return User.fromJson(jsonDecode(response.body));
}
// Error handling
Future<User?> safeFetchUser(int id) async {
try {
return await fetchUser(id);
} on HttpException catch (e) {
logger.error('HTTP error: ${e.message}');
return null;
} catch (e) {
logger.error('Unexpected error: $e');
return null;
}
}
```
### Parallel Execution
```dart
// Wait for all futures
Future<Dashboard> loadDashboard() async {
final results = await Future.wait([
fetchUsers(),
fetchOrders(),
fetchStats(),
]);
return Dashboard(
users: results[0] as List<User>,
orders: results[1] as List<Order>,
stats: results[2] as Stats,
);
}
// With typed results
Future<(List<User>, List<Order>)> loadData() async {
final (users, orders) = await (
fetchUsers(),
fetchOrders(),
).wait;
return (users, orders);
}
```
### Streams
```dart
// Stream creation
Stream<int> countStream(int max) async* {
for (var i = 0; i < max; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
// Stream transformation
Stream<String> userNames(Stream<User> users) {
return users.map((user) => user.name);
}
// Stream consumption
void listenToUsers() {
userStream.listen(
(user) => print('New user: ${user.name}'),
onError: (error) => print('Error: $error'),
onDone: () => print('Stream closed'),
);
}
```
## Widgets
### Stateless Widgets
```dart
class UserCard extends StatelessWidget {
const UserCard({
super.key,
required this.user,
this.onTap,
});
final User user;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(user.avatarUrl),
),
title: Text(user.name),
subtitle: Text(user.email),
onTap: onTap,
),
);
}
}
```
### Stateful Widgets
```dart
class Counter extends StatefulWidget {
const Counter({super.key, this.initialValue = 0});
final int initialValue;
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
late int _count;
@override
void initState() {
super.initState();
_count = widget.initialValue;
}
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $_count'),
ElevatedButton(
onPressed: _increment,
child: const Text('Increment'),
),
],
);
}
}
```
### Widget Best Practices
```dart
// Use const constructors
class MyWidget extends StatelessWidget {
const MyWidget({super.key}); // const constructor
@override
Widget build(BuildContext context) {
return const Column(
children: [
Text('Hello'), // const widget
SizedBox(height: 8), // const widget
],
);
}
}
// Extract widgets for reusability
class PrimaryButton extends StatelessWidget {
const PrimaryButton({
super.key,
required this.label,
required this.onPressed,
this.isLoading = false,
});
final String label;
final VoidCallback? onPressed;
final bool isLoading;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: isLoading ? null : onPressed,
child: isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(label),
);
}
}
```
## State Management
### Provider Pattern
```dart
// Model with ChangeNotifier
class CartModel extends ChangeNotifier {
final List<Item> _items = [];
List<Item> get items => List.unmodifiable(_items);
double get totalPrice => _items.fold(0, (sum, item) => sum + item.price);
void addItem(Item item) {
_items.add(item);
notifyListeners();
}
void removeItem(Item item) {
_items.remove(item);
notifyListeners();
}
}
// Provider setup
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CartModel(),
child: const MyApp(),
),
);
}
// Consuming provider
class CartPage extends StatelessWidget {
const CartPage({super.key});
@override
Widget build(BuildContext context) {
return Consumer<CartModel>(
builder: (context, cart, child) {
return ListView.builder(
itemCount: cart.items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(cart.items[index].name),
);
},
);
},
);
}
}
```
### Riverpod Pattern
```dart
// Provider definition
final userProvider = FutureProvider<User>((ref) async {
final repository = ref.read(userRepositoryProvider);
return repository.fetchCurrentUser();
});
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
}
// Consumer widget
class UserProfile extends ConsumerWidget {
const UserProfile({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final userAsync = ref.watch(userProvider);
return userAsync.when(
data: (user) => Text('Hello, ${user.name}'),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text('Error: $error'),
);
}
}
```
### BLoC Pattern
```dart
// Events
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
// State
class CounterState {
final int count;
const CounterState(this.count);
}
// BLoC
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(const CounterState(0)) {
on<IncrementEvent>((event, emit) {
emit(CounterState(state.count + 1));
});
on<DecrementEvent>((event, emit) {
emit(CounterState(state.count - 1));
});
}
}
// Usage
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text('Count: ${state.count}');
},
);
}
}
```
## Testing
### Unit Tests
```dart
import 'package:test/test.dart';
void main() {
group('Calculator', () {
late Calculator calculator;
setUp(() {
calculator = Calculator();
});
test('adds two positive numbers', () {
expect(calculator.add(2, 3), equals(5));
});
test('handles negative numbers', () {
expect(calculator.add(-1, 1), equals(0));
});
});
}
```
### Widget Tests
```dart
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Counter increments', (WidgetTester tester) async {
// Build widget
await tester.pumpWidget(const MaterialApp(home: Counter()));
// Verify initial state
expect(find.text('Count: 0'), findsOneWidget);
// Tap increment button
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify incremented state
expect(find.text('Count: 1'), findsOneWidget);
});
testWidgets('shows loading indicator', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: UserProfile(isLoading: true),
),
);
expect(find.byType(CircularProgressIndicator), findsOneWidget);
});
}
```
### Mocking
```dart
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
@GenerateMocks([UserRepository])
void main() {
late MockUserRepository mockRepository;
late UserService service;
setUp(() {
mockRepository = MockUserRepository();
service = UserService(mockRepository);
});
test('fetches user by id', () async {
final user = User(id: 1, name: 'John');
when(mockRepository.findById(1)).thenAnswer((_) async => user);
final result = await service.getUser(1);
expect(result, equals(user));
verify(mockRepository.findById(1)).called(1);
});
}
```
## Common Patterns
### Factory Constructors
```dart
class User {
final int id;
final String name;
final String email;
const User({
required this.id,
required this.name,
required this.email,
});
// Factory from JSON
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
email: json['email'] as String,
);
}
// Factory for default user
factory User.guest() {
return const User(
id: 0,
name: 'Guest',
email: 'guest@example.com',
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
};
}
}
```
### Extension Methods
```dart
extension StringExtensions on String {
String capitalize() {
if (isEmpty) return this;
return '${this[0].toUpperCase()}${substring(1)}';
}
bool get isValidEmail {
return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(this);
}
}
extension DateTimeExtensions on DateTime {
String get formatted => '${day.toString().padLeft(2, '0')}/'
'${month.toString().padLeft(2, '0')}/$year';
bool get isToday {
final now = DateTime.now();
return year == now.year && month == now.month && day == now.day;
}
}
// Usage
final name = 'john'.capitalize(); // 'John'
final isValid = 'test@example.com'.isValidEmail; // true
```
### Sealed Classes (Dart 3)
```dart
sealed class Result<T> {}
class Success<T> extends Result<T> {
final T data;
Success(this.data);
}
class Error<T> extends Result<T> {
final String message;
Error(this.message);
}
class Loading<T> extends Result<T> {}
// Usage with exhaustive pattern matching
Widget buildResult(Result<User> result) {
return switch (result) {
Success(data: final user) => Text(user.name),
Error(message: final msg) => Text('Error: $msg'),
Loading() => const CircularProgressIndicator(),
};
}
```
### Freezed for Immutable Data
```dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user.freezed.dart';
part 'user.g.dart';
@freezed
class User with _$User {
const factory User({
required int id,
required String name,
required String email,
@Default(false) bool isActive,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
// Usage
final user = User(id: 1, name: 'John', email: 'john@example.com');
final updatedUser = user.copyWith(name: 'Jane');
```
## Project Structure
### Feature-Based Organization
```
lib/
├── main.dart
├── app.dart
├── core/
│ ├── constants/
│ ├── extensions/
│ ├── utils/
│ └── widgets/
├── features/
│ ├── auth/
│ │ ├── data/
│ │ ├── domain/
│ │ └── presentation/
│ ├── home/
│ │ ├── data/
│ │ ├── domain/
│ │ └── presentation/
│ └── profile/
└── shared/
├── models/
├── services/
└── widgets/
```
### Naming Conventions
```dart
// Files: snake_case
// user_repository.dart
// home_screen.dart
// Classes: PascalCase
class UserRepository {}
class HomeScreen extends StatelessWidget {}
// Variables and functions: camelCase
final userName = 'John';
void fetchUserData() {}
// Constants: camelCase or SCREAMING_SNAKE_CASE
const defaultPadding = 16.0;
const API_BASE_URL = 'https://api.example.com';
// Private: underscore prefix
class _HomeScreenState extends State<HomeScreen> {}
final _internalCache = <String, dynamic>{};
```

View File

@@ -0,0 +1,235 @@
# General Code Style Guide
Universal coding principles that apply across all languages and frameworks.
## Readability
### Code is Read More Than Written
- Write code for humans first, computers second
- Favor clarity over cleverness
- If code needs a comment to explain what it does, consider rewriting it
### Formatting
- Consistent indentation (use project standard)
- Reasonable line length (80-120 characters)
- Logical grouping with whitespace
- One statement per line
### Structure
- Keep functions/methods short (ideally < 20 lines)
- One level of abstraction per function
- Early returns to reduce nesting
- Group related code together
## Naming Conventions
### General Principles
- Names should reveal intent
- Avoid abbreviations (except universally understood ones)
- Be consistent within codebase
- Length proportional to scope
### Variables
```
# Bad
d = 86400 # What is this?
temp = getUserData() # Temp what?
# Good
secondsPerDay = 86400
userData = getUserData()
```
### Functions/Methods
- Use verbs for actions: `calculateTotal()`, `validateInput()`
- Use `is/has/can` for booleans: `isValid()`, `hasPermission()`
- Be specific: `sendEmailNotification()` not `send()`
### Constants
- Use SCREAMING_SNAKE_CASE or language convention
- Group related constants
- Document magic numbers
### Classes/Types
- Use nouns: `User`, `OrderProcessor`, `ValidationResult`
- Avoid generic names: `Manager`, `Handler`, `Data`
## Comments
### When to Comment
- WHY, not WHAT (code shows what, comments explain why)
- Complex algorithms or business logic
- Non-obvious workarounds with references
- Public API documentation
### When NOT to Comment
- Obvious code
- Commented-out code (delete it)
- Change history (use git)
- TODOs without tickets (create tickets instead)
### Comment Quality
```
# Bad
i += 1 # Increment i
# Good
# Retry limit based on SLA requirements (see JIRA-1234)
maxRetries = 3
```
## Error Handling
### Principles
- Fail fast and explicitly
- Handle errors at appropriate level
- Preserve error context
- Log for debugging, throw for callers
### Patterns
```
# Bad: Silent failure
try:
result = riskyOperation()
except:
pass
# Good: Explicit handling
try:
result = riskyOperation()
except SpecificError as e:
logger.error(f"Operation failed: {e}")
raise OperationFailed("Unable to complete operation") from e
```
### Error Messages
- Be specific about what failed
- Include relevant context
- Suggest remediation when possible
- Avoid exposing internal details to users
## Functions and Methods
### Single Responsibility
- One function = one task
- If you need "and" to describe it, split it
- Extract helper functions for clarity
### Parameters
- Limit parameters (ideally ≤ 3)
- Use objects/structs for many parameters
- Avoid boolean parameters (use named options)
- Order: required first, optional last
### Return Values
- Return early for edge cases
- Consistent return types
- Avoid returning null/nil when possible
- Consider Result/Option types for failures
## Code Organization
### File Structure
- One primary concept per file
- Related helpers in same file or nearby
- Consistent file naming
- Logical directory structure
### Import/Dependency Order
1. Standard library
2. External dependencies
3. Internal dependencies
4. Local/relative imports
### Coupling and Cohesion
- High cohesion within modules
- Low coupling between modules
- Depend on abstractions, not implementations
- Avoid circular dependencies
## Testing Considerations
### Testable Code
- Pure functions where possible
- Dependency injection
- Avoid global state
- Small, focused functions
### Test Naming
```
# Describe behavior, not implementation
test_user_can_login_with_valid_credentials()
test_order_total_includes_tax_and_shipping()
```
## Security Basics
### Input Validation
- Validate all external input
- Sanitize before use
- Whitelist over blacklist
- Fail closed (deny by default)
### Secrets
- Never hardcode secrets
- Use environment variables or secret managers
- Don't log sensitive data
- Rotate credentials regularly
### Data Handling
- Minimize data collection
- Encrypt sensitive data
- Secure data in transit and at rest
- Follow principle of least privilege
## Performance Mindset
### Premature Optimization
- Make it work, then make it fast
- Measure before optimizing
- Optimize bottlenecks, not everything
- Document performance-critical code
### Common Pitfalls
- N+1 queries
- Unnecessary allocations in loops
- Missing indexes
- Synchronous operations that could be async
## Code Review Checklist
- [ ] Does it work correctly?
- [ ] Is it readable and maintainable?
- [ ] Are edge cases handled?
- [ ] Is error handling appropriate?
- [ ] Are there security concerns?
- [ ] Is it tested adequately?
- [ ] Does it follow project conventions?
- [ ] Is there unnecessary complexity?

View File

@@ -0,0 +1,562 @@
# Go Style Guide
Go idioms and conventions for clean, maintainable code.
## gofmt and Standard Formatting
### Always Use gofmt
```bash
# Format a single file
gofmt -w file.go
# Format entire project
gofmt -w .
# Use goimports for imports management
goimports -w .
```
### Formatting Rules (Enforced by gofmt)
- Tabs for indentation
- No trailing whitespace
- Consistent brace placement
- Standardized spacing
## Error Handling
### Explicit Error Checking
```go
// Always check errors explicitly
file, err := os.Open(filename)
if err != nil {
return fmt.Errorf("opening file %s: %w", filename, err)
}
defer file.Close()
// Don't ignore errors with _
// Bad
data, _ := json.Marshal(obj)
// Good
data, err := json.Marshal(obj)
if err != nil {
return nil, fmt.Errorf("marshaling object: %w", err)
}
```
### Error Wrapping
```go
// Use %w to wrap errors for unwrapping later
func processFile(path string) error {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("reading file %s: %w", path, err)
}
if err := validate(data); err != nil {
return fmt.Errorf("validating data: %w", err)
}
return nil
}
// Check wrapped errors
if errors.Is(err, os.ErrNotExist) {
// Handle file not found
}
var validationErr *ValidationError
if errors.As(err, &validationErr) {
// Handle validation error
}
```
### Custom Error Types
```go
// Sentinel errors for expected conditions
var (
ErrNotFound = errors.New("resource not found")
ErrUnauthorized = errors.New("unauthorized access")
ErrInvalidInput = errors.New("invalid input")
)
// Custom error type with additional context
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}
// Error constructor
func NewValidationError(field, message string) error {
return &ValidationError{Field: field, Message: message}
}
```
## Interfaces
### Small, Focused Interfaces
```go
// Good: Single-method interface
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// Compose interfaces
type ReadWriter interface {
Reader
Writer
}
// Bad: Large interfaces
type Repository interface {
Find(id string) (*User, error)
FindAll() ([]*User, error)
Create(user *User) error
Update(user *User) error
Delete(id string) error
FindByEmail(email string) (*User, error)
// Too many methods - hard to implement and test
}
```
### Accept Interfaces, Return Structs
```go
// Good: Accept interface, return concrete type
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}
// Interface defined by consumer
type UserRepository interface {
Find(ctx context.Context, id string) (*User, error)
Save(ctx context.Context, user *User) error
}
// Concrete implementation
type PostgresUserRepo struct {
db *sql.DB
}
func (r *PostgresUserRepo) Find(ctx context.Context, id string) (*User, error) {
// Implementation
}
```
### Interface Naming
```go
// Single-method interfaces: method name + "er"
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
type Closer interface { Close() error }
type Stringer interface { String() string }
// Multi-method interfaces: descriptive name
type UserStore interface {
Get(ctx context.Context, id string) (*User, error)
Put(ctx context.Context, user *User) error
}
```
## Package Structure
### Standard Layout
```
myproject/
├── cmd/
│ └── myapp/
│ └── main.go # Application entry point
├── internal/
│ ├── auth/
│ │ ├── auth.go
│ │ └── auth_test.go
│ ├── user/
│ │ ├── user.go
│ │ ├── repository.go
│ │ └── service.go
│ └── config/
│ └── config.go
├── pkg/ # Public packages (optional)
│ └── api/
│ └── client.go
├── go.mod
├── go.sum
└── README.md
```
### Package Guidelines
```go
// Package names: short, lowercase, no underscores
package user // Good
package userService // Bad
package user_service // Bad
// Package comment at top of primary file
// Package user provides user management functionality.
package user
// Group imports: stdlib, external, internal
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/lib/pq"
"myproject/internal/config"
)
```
### Internal Packages
```go
// internal/ packages cannot be imported from outside the module
// Use for implementation details you don't want to expose
// myproject/internal/cache/cache.go
package cache
// This can only be imported by code in myproject/
```
## Testing
### Test File Organization
```go
// user_test.go - same package
package user
import (
"testing"
)
func TestUserValidation(t *testing.T) {
// Test implementation details
}
// user_integration_test.go - external test package
package user_test
import (
"testing"
"myproject/internal/user"
)
func TestUserService(t *testing.T) {
// Test public API
}
```
### Table-Driven Tests
```go
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -1, -1, -2},
{"mixed numbers", -1, 5, 4},
{"zeros", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d",
tt.a, tt.b, result, tt.expected)
}
})
}
}
```
### Test Helpers
```go
// Helper functions should call t.Helper()
func newTestUser(t *testing.T) *User {
t.Helper()
return &User{
ID: uuid.New().String(),
Name: "Test User",
Email: "test@example.com",
}
}
func assertNoError(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func assertEqual[T comparable](t *testing.T, got, want T) {
t.Helper()
if got != want {
t.Errorf("got %v; want %v", got, want)
}
}
```
### Mocking with Interfaces
```go
// Define interface for dependency
type UserRepository interface {
Find(ctx context.Context, id string) (*User, error)
Save(ctx context.Context, user *User) error
}
// Mock implementation for testing
type mockUserRepo struct {
users map[string]*User
}
func newMockUserRepo() *mockUserRepo {
return &mockUserRepo{users: make(map[string]*User)}
}
func (m *mockUserRepo) Find(ctx context.Context, id string) (*User, error) {
user, ok := m.users[id]
if !ok {
return nil, ErrNotFound
}
return user, nil
}
func (m *mockUserRepo) Save(ctx context.Context, user *User) error {
m.users[user.ID] = user
return nil
}
// Test using mock
func TestUserService_GetUser(t *testing.T) {
repo := newMockUserRepo()
repo.users["123"] = &User{ID: "123", Name: "Test"}
service := NewUserService(repo)
user, err := service.GetUser(context.Background(), "123")
assertNoError(t, err)
assertEqual(t, user.Name, "Test")
}
```
## Common Patterns
### Options Pattern
```go
// Option function type
type ServerOption func(*Server)
// Option functions
func WithPort(port int) ServerOption {
return func(s *Server) {
s.port = port
}
}
func WithTimeout(timeout time.Duration) ServerOption {
return func(s *Server) {
s.timeout = timeout
}
}
func WithLogger(logger *slog.Logger) ServerOption {
return func(s *Server) {
s.logger = logger
}
}
// Constructor using options
func NewServer(opts ...ServerOption) *Server {
s := &Server{
port: 8080, // defaults
timeout: 30 * time.Second,
logger: slog.Default(),
}
for _, opt := range opts {
opt(s)
}
return s
}
// Usage
server := NewServer(
WithPort(9000),
WithTimeout(time.Minute),
)
```
### Context Usage
```go
// Always pass context as first parameter
func (s *Service) ProcessRequest(ctx context.Context, req *Request) (*Response, error) {
// Check for cancellation
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
// Use context for timeouts
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
result, err := s.repo.Find(ctx, req.ID)
if err != nil {
return nil, fmt.Errorf("finding item: %w", err)
}
return &Response{Data: result}, nil
}
```
### Defer for Cleanup
```go
func processFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close() // Always executed on return
// Process file...
return nil
}
// Multiple defers execute in LIFO order
func transaction(db *sql.DB) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback() // Safe: no-op if committed
// Do work...
return tx.Commit()
}
```
### Concurrency Patterns
```go
// Worker pool
func processItems(items []Item, workers int) []Result {
jobs := make(chan Item, len(items))
results := make(chan Result, len(items))
// Start workers
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for item := range jobs {
results <- process(item)
}
}()
}
// Send jobs
for _, item := range items {
jobs <- item
}
close(jobs)
// Wait and collect
go func() {
wg.Wait()
close(results)
}()
var out []Result
for r := range results {
out = append(out, r)
}
return out
}
```
## Code Quality
### Linting with golangci-lint
```yaml
# .golangci.yml
linters:
enable:
- errcheck
- govet
- ineffassign
- staticcheck
- unused
- gosimple
- gocritic
- gofmt
- goimports
linters-settings:
govet:
check-shadowing: true
errcheck:
check-type-assertions: true
issues:
exclude-rules:
- path: _test\.go
linters:
- errcheck
```
### Common Commands
```bash
# Format code
go fmt ./...
# Run linter
golangci-lint run
# Run tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Check for race conditions
go test -race ./...
# Build
go build ./...
```

View File

@@ -0,0 +1,618 @@
# HTML & CSS Style Guide
Web standards for semantic markup, maintainable styling, and accessibility.
## Semantic HTML
### Document Structure
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Page description for SEO" />
<title>Page Title | Site Name</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<header>
<nav aria-label="Main navigation">
<!-- Navigation -->
</nav>
</header>
<main>
<article>
<!-- Primary content -->
</article>
<aside>
<!-- Supplementary content -->
</aside>
</main>
<footer>
<!-- Footer content -->
</footer>
</body>
</html>
```
### Semantic Elements
```html
<!-- Use appropriate semantic elements -->
<!-- Navigation -->
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<!-- Article with header and footer -->
<article>
<header>
<h1>Article Title</h1>
<time datetime="2024-01-15">January 15, 2024</time>
</header>
<p>Article content...</p>
<footer>
<p>Written by <address>Author Name</address></p>
</footer>
</article>
<!-- Sections with headings -->
<section aria-labelledby="features-heading">
<h2 id="features-heading">Features</h2>
<p>Section content...</p>
</section>
<!-- Figures with captions -->
<figure>
<img src="chart.png" alt="Sales data showing 20% growth">
<figcaption>Q4 2024 Sales Performance</figcaption>
</figure>
<!-- Definition lists -->
<dl>
<dt>HTML</dt>
<dd>HyperText Markup Language</dd>
<dt>CSS</dt>
<dd>Cascading Style Sheets</dd>
</dl>
```
### Form Elements
```html
<form action="/submit" method="POST">
<!-- Text input with label -->
<div class="form-group">
<label for="email">Email Address</label>
<input
type="email"
id="email"
name="email"
required
autocomplete="email"
aria-describedby="email-hint"
/>
<span id="email-hint" class="hint">We'll never share your email.</span>
</div>
<!-- Select with label -->
<div class="form-group">
<label for="country">Country</label>
<select id="country" name="country" required>
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
</select>
</div>
<!-- Radio group with fieldset -->
<fieldset>
<legend>Preferred Contact Method</legend>
<div>
<input type="radio" id="contact-email" name="contact" value="email" />
<label for="contact-email">Email</label>
</div>
<div>
<input type="radio" id="contact-phone" name="contact" value="phone" />
<label for="contact-phone">Phone</label>
</div>
</fieldset>
<!-- Submit button -->
<button type="submit">Submit</button>
</form>
```
## BEM Naming Convention
### Block, Element, Modifier
```css
/* Block: Standalone component */
.card {
}
/* Element: Part of block (double underscore) */
.card__header {
}
.card__body {
}
.card__footer {
}
/* Modifier: Variation (double hyphen) */
.card--featured {
}
.card--compact {
}
.card__header--centered {
}
```
### BEM Examples
```html
<!-- Card component -->
<article class="card card--featured">
<header class="card__header">
<h2 class="card__title">Card Title</h2>
</header>
<div class="card__body">
<p class="card__text">Card content goes here.</p>
</div>
<footer class="card__footer">
<button class="card__button card__button--primary">Action</button>
</footer>
</article>
<!-- Navigation component -->
<nav class="nav nav--horizontal">
<ul class="nav__list">
<li class="nav__item nav__item--active">
<a class="nav__link" href="/">Home</a>
</li>
<li class="nav__item">
<a class="nav__link" href="/about">About</a>
</li>
</ul>
</nav>
```
### BEM Best Practices
```css
/* Avoid deep nesting */
/* Bad */
.card__header__title__icon {
}
/* Good - flatten structure */
.card__title-icon {
}
/* Avoid styling elements without class */
/* Bad */
.card h2 {
}
/* Good */
.card__title {
}
/* Modifiers extend base styles */
.button {
padding: 8px 16px;
border-radius: 4px;
}
.button--large {
padding: 12px 24px;
}
.button--primary {
background: blue;
color: white;
}
```
## Accessibility
### ARIA Attributes
```html
<!-- Live regions for dynamic content -->
<div aria-live="polite" aria-atomic="true">Status updates appear here</div>
<!-- Landmarks -->
<nav aria-label="Main navigation"></nav>
<nav aria-label="Footer navigation"></nav>
<!-- Current page in navigation -->
<a href="/about" aria-current="page">About</a>
<!-- Expanded/collapsed state -->
<button aria-expanded="false" aria-controls="menu">Toggle Menu</button>
<div id="menu" hidden>Menu content</div>
<!-- Disabled vs aria-disabled -->
<button disabled>Can't click (removed from tab order)</button>
<button aria-disabled="true">Can't click (stays in tab order)</button>
<!-- Loading states -->
<button aria-busy="true">
<span aria-hidden="true">Loading...</span>
<span class="visually-hidden">Please wait</span>
</button>
```
### Keyboard Navigation
```html
<!-- Skip link -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<!-- Focusable elements should be obvious -->
<style>
:focus-visible {
outline: 2px solid blue;
outline-offset: 2px;
}
</style>
<!-- Tabindex usage -->
<!-- tabindex="0": Add to tab order -->
<div tabindex="0" role="button">Custom button</div>
<!-- tabindex="-1": Programmatically focusable only -->
<div id="modal" tabindex="-1">Modal content</div>
<!-- Never use tabindex > 0 -->
```
### Screen Reader Support
```css
/* Visually hidden but accessible */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Hide from screen readers */
[aria-hidden="true"] {
/* Decorative content */
}
```
```html
<!-- Icon buttons need accessible names -->
<button aria-label="Close dialog">
<svg aria-hidden="true"><!-- icon --></svg>
</button>
<!-- Decorative images -->
<img src="decoration.png" alt="" role="presentation" />
<!-- Informative images -->
<img src="chart.png" alt="Sales increased 20% in Q4 2024" />
<!-- Complex images -->
<figure>
<img
src="flowchart.png"
alt="User registration process"
aria-describedby="flowchart-desc"
/>
<figcaption id="flowchart-desc">
Step 1: Enter email. Step 2: Verify email. Step 3: Create password.
</figcaption>
</figure>
```
## Responsive Design
### Mobile-First Approach
```css
/* Base styles for mobile */
.container {
padding: 16px;
}
.grid {
display: grid;
gap: 16px;
grid-template-columns: 1fr;
}
/* Tablet and up */
@media (min-width: 768px) {
.container {
padding: 24px;
}
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop and up */
@media (min-width: 1024px) {
.container {
padding: 32px;
max-width: 1200px;
margin: 0 auto;
}
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
```
### Flexible Units
```css
/* Use relative units */
body {
font-size: 16px; /* Base size */
}
h1 {
font-size: 2rem; /* Relative to root */
margin-bottom: 1em; /* Relative to element */
}
.container {
max-width: 75ch; /* Character width for readability */
padding: 1rem;
}
/* Fluid typography */
h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
}
/* Fluid spacing */
.section {
padding: clamp(2rem, 5vw, 4rem);
}
```
### Responsive Images
```html
<!-- Responsive image with srcset -->
<img
src="image-800.jpg"
srcset="image-400.jpg 400w, image-800.jpg 800w, image-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="Description"
loading="lazy"
/>
<!-- Art direction with picture -->
<picture>
<source media="(min-width: 1024px)" srcset="hero-desktop.jpg" />
<source media="(min-width: 768px)" srcset="hero-tablet.jpg" />
<img src="hero-mobile.jpg" alt="Hero image" />
</picture>
```
## CSS Best Practices
### Custom Properties (CSS Variables)
```css
:root {
/* Colors */
--color-primary: #0066cc;
--color-primary-dark: #004c99;
--color-secondary: #6c757d;
--color-success: #28a745;
--color-error: #dc3545;
/* Typography */
--font-family-base: system-ui, sans-serif;
--font-family-mono: ui-monospace, monospace;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.25rem;
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* Borders */
--border-radius: 4px;
--border-color: #dee2e6;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root {
--color-primary: #4da6ff;
--color-background: #1a1a1a;
--color-text: #ffffff;
}
}
/* Usage */
.button {
background: var(--color-primary);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius);
}
```
### Modern Layout
```css
/* Flexbox for 1D layouts */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--spacing-md);
}
/* Grid for 2D layouts */
.page-layout {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
.header {
grid-area: header;
}
.sidebar {
grid-area: sidebar;
}
.main {
grid-area: main;
}
.footer {
grid-area: footer;
}
/* Auto-fit grid */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--spacing-lg);
}
```
### Performance
```css
/* Avoid expensive properties in animations */
/* Bad - triggers layout */
.animate-bad {
animation: move 1s;
}
@keyframes move {
to {
left: 100px;
top: 100px;
}
}
/* Good - uses transform */
.animate-good {
animation: move-optimized 1s;
}
@keyframes move-optimized {
to {
transform: translate(100px, 100px);
}
}
/* Use will-change sparingly */
.will-animate {
will-change: transform;
}
/* Contain for layout isolation */
.card {
contain: layout style;
}
/* Content-visibility for off-screen content */
.below-fold {
content-visibility: auto;
contain-intrinsic-size: 500px;
}
```
## HTML Best Practices
### Validation and Attributes
```html
<!-- Use proper input types -->
<input type="email" autocomplete="email" />
<input type="tel" autocomplete="tel" />
<input type="url" />
<input type="number" min="0" max="100" step="1" />
<input type="date" min="2024-01-01" />
<!-- Required and validation -->
<input type="text" required minlength="2" maxlength="50" pattern="[A-Za-z]+" />
<!-- Autocomplete for better UX -->
<input type="text" name="name" autocomplete="name" />
<input type="text" name="address" autocomplete="street-address" />
<input type="text" name="cc-number" autocomplete="cc-number" />
```
### Performance Attributes
```html
<!-- Lazy loading -->
<img src="image.jpg" loading="lazy" alt="Description" />
<iframe src="video.html" loading="lazy"></iframe>
<!-- Preload critical resources -->
<link rel="preload" href="critical.css" as="style" />
<link rel="preload" href="hero.jpg" as="image" />
<link rel="preload" href="font.woff2" as="font" crossorigin />
<!-- Preconnect to origins -->
<link rel="preconnect" href="https://api.example.com" />
<link rel="dns-prefetch" href="https://analytics.example.com" />
<!-- Async/defer scripts -->
<script src="analytics.js" async></script>
<script src="app.js" defer></script>
```
### Microdata and SEO
```html
<!-- Schema.org markup -->
<article itemscope itemtype="https://schema.org/Article">
<h1 itemprop="headline">Article Title</h1>
<time itemprop="datePublished" datetime="2024-01-15"> January 15, 2024 </time>
<div itemprop="author" itemscope itemtype="https://schema.org/Person">
<span itemprop="name">Author Name</span>
</div>
<div itemprop="articleBody">Article content...</div>
</article>
<!-- Open Graph for social sharing -->
<meta property="og:title" content="Page Title" />
<meta property="og:description" content="Page description" />
<meta property="og:image" content="https://example.com/image.jpg" />
<meta property="og:url" content="https://example.com/page" />
```

View File

@@ -0,0 +1,569 @@
# JavaScript Style Guide
Modern JavaScript (ES6+) best practices and conventions.
## ES6+ Features
### Use Modern Syntax
```javascript
// Prefer const and let over var
const immutableValue = "fixed";
let mutableValue = "can change";
// Never use var
// var outdated = 'avoid this';
// Template literals over concatenation
const greeting = `Hello, ${name}!`;
// Destructuring
const { id, name, email } = user;
const [first, second, ...rest] = items;
// Spread operator
const merged = { ...defaults, ...options };
const combined = [...array1, ...array2];
// Arrow functions for short callbacks
const doubled = numbers.map((n) => n * 2);
```
### Object Shorthand
```javascript
// Property shorthand
const name = "John";
const age = 30;
const user = { name, age };
// Method shorthand
const calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
},
};
// Computed property names
const key = "dynamic";
const obj = {
[key]: "value",
[`${key}Method`]() {
return "result";
},
};
```
### Default Parameters and Rest
```javascript
// Default parameters
function greet(name = "Guest", greeting = "Hello") {
return `${greeting}, ${name}!`;
}
// Rest parameters
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
// Named parameters via destructuring
function createUser({ name, email, role = "user" }) {
return { name, email, role, createdAt: new Date() };
}
```
## Async/Await
### Prefer async/await Over Promises
```javascript
// Bad: Promise chains
function fetchUserPosts(userId) {
return fetch(`/users/${userId}`)
.then((res) => res.json())
.then((user) => fetch(`/posts?userId=${user.id}`))
.then((res) => res.json());
}
// Good: async/await
async function fetchUserPosts(userId) {
const userRes = await fetch(`/users/${userId}`);
const user = await userRes.json();
const postsRes = await fetch(`/posts?userId=${user.id}`);
return postsRes.json();
}
```
### Parallel Execution
```javascript
// Sequential (slow)
async function loadDataSequentially() {
const users = await fetchUsers();
const posts = await fetchPosts();
const comments = await fetchComments();
return { users, posts, comments };
}
// Parallel (fast)
async function loadDataParallel() {
const [users, posts, comments] = await Promise.all([
fetchUsers(),
fetchPosts(),
fetchComments(),
]);
return { users, posts, comments };
}
```
### Error Handling
```javascript
// try/catch with async/await
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error("Fetch failed:", error.message);
throw error;
}
}
// Error handling utility
async function safeAsync(promise) {
try {
const result = await promise;
return [result, null];
} catch (error) {
return [null, error];
}
}
// Usage
const [data, error] = await safeAsync(fetchData("/api/users"));
if (error) {
handleError(error);
}
```
## Error Handling
### Custom Errors
```javascript
class AppError extends Error {
constructor(message, code, statusCode = 500) {
super(message);
this.name = "AppError";
this.code = code;
this.statusCode = statusCode;
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message, field) {
super(message, "VALIDATION_ERROR", 400);
this.name = "ValidationError";
this.field = field;
}
}
class NotFoundError extends AppError {
constructor(resource, id) {
super(`${resource} with id ${id} not found`, "NOT_FOUND", 404);
this.name = "NotFoundError";
this.resource = resource;
this.resourceId = id;
}
}
```
### Error Handling Patterns
```javascript
// Centralized error handler
function handleError(error) {
if (error instanceof ValidationError) {
showFieldError(error.field, error.message);
} else if (error instanceof NotFoundError) {
showNotFound(error.resource);
} else {
showGenericError("Something went wrong");
reportError(error);
}
}
// Error boundary pattern (for React)
function withErrorBoundary(Component) {
return class extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
reportError(error, info);
}
render() {
if (this.state.hasError) {
return <ErrorFallback />;
}
return <Component {...this.props} />;
}
};
}
```
## Module Patterns
### ES Modules
```javascript
// Named exports
export const API_URL = "/api";
export function fetchData(endpoint) {
/* ... */
}
export class ApiClient {
/* ... */
}
// Re-exports
export { User, Post } from "./types.js";
export * as utils from "./utils.js";
// Imports
import { fetchData, API_URL } from "./api.js";
import * as api from "./api.js";
import defaultExport from "./module.js";
```
### Module Organization
```javascript
// Feature-based organization
// features/user/
// index.js - Public exports
// api.js - API calls
// utils.js - Helper functions
// constants.js - Feature constants
// index.js - Barrel export
export { UserService } from "./service.js";
export { validateUser } from "./utils.js";
export { USER_ROLES } from "./constants.js";
```
### Dependency Injection
```javascript
// Constructor injection
class UserService {
constructor(apiClient, logger) {
this.api = apiClient;
this.logger = logger;
}
async getUser(id) {
this.logger.info(`Fetching user ${id}`);
return this.api.get(`/users/${id}`);
}
}
// Factory function
function createUserService(config = {}) {
const api = config.apiClient || new ApiClient();
const logger = config.logger || console;
return new UserService(api, logger);
}
```
## Functional Patterns
### Pure Functions
```javascript
// Impure: Modifies external state
let count = 0;
function incrementCount() {
count++;
return count;
}
// Pure: No side effects
function increment(value) {
return value + 1;
}
// Pure: Same input = same output
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
```
### Array Methods
```javascript
const users = [
{ id: 1, name: "Alice", active: true },
{ id: 2, name: "Bob", active: false },
{ id: 3, name: "Charlie", active: true },
];
// map - transform
const names = users.map((user) => user.name);
// filter - select
const activeUsers = users.filter((user) => user.active);
// find - first match
const user = users.find((user) => user.id === 2);
// some/every - boolean check
const hasActive = users.some((user) => user.active);
const allActive = users.every((user) => user.active);
// reduce - accumulate
const userMap = users.reduce((map, user) => {
map[user.id] = user;
return map;
}, {});
// Chaining
const activeNames = users
.filter((user) => user.active)
.map((user) => user.name)
.sort();
```
### Composition
```javascript
// Compose functions
const compose =
(...fns) =>
(x) =>
fns.reduceRight((acc, fn) => fn(acc), x);
const pipe =
(...fns) =>
(x) =>
fns.reduce((acc, fn) => fn(acc), x);
// Usage
const processUser = pipe(validateUser, normalizeUser, enrichUser);
const result = processUser(rawUserData);
```
## Classes
### Modern Class Syntax
```javascript
class User {
// Private fields
#password;
// Static properties
static ROLES = ["admin", "user", "guest"];
constructor(name, email) {
this.name = name;
this.email = email;
this.#password = null;
}
// Getter
get displayName() {
return `${this.name} <${this.email}>`;
}
// Setter
set password(value) {
if (value.length < 8) {
throw new Error("Password too short");
}
this.#password = hashPassword(value);
}
// Instance method
toJSON() {
return { name: this.name, email: this.email };
}
// Static method
static fromJSON(json) {
return new User(json.name, json.email);
}
}
```
### Inheritance
```javascript
class Entity {
constructor(id) {
this.id = id;
this.createdAt = new Date();
}
equals(other) {
return other instanceof Entity && this.id === other.id;
}
}
class User extends Entity {
constructor(id, name, email) {
super(id);
this.name = name;
this.email = email;
}
toJSON() {
return {
id: this.id,
name: this.name,
email: this.email,
createdAt: this.createdAt.toISOString(),
};
}
}
```
## Common Patterns
### Null Safety
```javascript
// Optional chaining
const city = user?.address?.city;
const firstItem = items?.[0];
const result = obj?.method?.();
// Nullish coalescing
const name = user.name ?? "Anonymous";
const count = value ?? 0;
// Combining both
const displayName = user?.profile?.name ?? "Unknown";
```
### Debounce and Throttle
```javascript
function debounce(fn, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
function throttle(fn, limit) {
let inThrottle;
return function (...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
```
### Memoization
```javascript
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// Usage
const expensiveCalculation = memoize((n) => {
// Complex computation
return fibonacci(n);
});
```
## Best Practices
### Avoid Common Pitfalls
```javascript
// Avoid loose equality
// Bad
if (value == null) {
}
// Good
if (value === null || value === undefined) {
}
if (value == null) {
} // Only acceptable for null/undefined check
// Avoid implicit type coercion
// Bad
if (items.length) {
}
// Good
if (items.length > 0) {
}
// Avoid modifying function arguments
// Bad
function process(options) {
options.processed = true;
return options;
}
// Good
function process(options) {
return { ...options, processed: true };
}
```
### Performance Tips
```javascript
// Avoid creating functions in loops
// Bad
items.forEach(function (item) {
item.addEventListener("click", function () {});
});
// Good
function handleClick(event) {}
items.forEach((item) => {
item.addEventListener("click", handleClick);
});
// Use appropriate data structures
// For frequent lookups, use Map/Set instead of Array
const userMap = new Map(users.map((u) => [u.id, u]));
const userIds = new Set(users.map((u) => u.id));
```

View File

@@ -0,0 +1,566 @@
# Python Style Guide
Python conventions following PEP 8 and modern best practices.
## PEP 8 Fundamentals
### Naming Conventions
```python
# Variables and functions: snake_case
user_name = "John"
def calculate_total(items):
pass
# Constants: SCREAMING_SNAKE_CASE
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30
# Classes: PascalCase
class UserAccount:
pass
# Private: single underscore prefix
class User:
def __init__(self):
self._internal_state = {}
# Name mangling: double underscore prefix
class Base:
def __init__(self):
self.__private = "truly private"
# Module-level "private": single underscore
_module_cache = {}
```
### Indentation and Line Length
```python
# 4 spaces per indentation level
def function():
if condition:
do_something()
# Line length: 88 characters (Black) or 79 (PEP 8)
# Break long lines appropriately
result = some_function(
argument_one,
argument_two,
argument_three,
)
# Implicit line continuation in brackets
users = [
"alice",
"bob",
"charlie",
]
```
### Imports
```python
# Standard library
import os
import sys
from pathlib import Path
from typing import Optional, List
# Third-party
import requests
from pydantic import BaseModel
# Local application
from myapp.models import User
from myapp.utils import format_date
# Avoid wildcard imports
# Bad: from module import *
# Good: from module import specific_item
```
## Type Hints
### Basic Type Annotations
```python
from typing import Optional, List, Dict, Tuple, Union, Any
# Variables
name: str = "John"
age: int = 30
active: bool = True
scores: List[int] = [90, 85, 92]
# Functions
def greet(name: str) -> str:
return f"Hello, {name}!"
def find_user(user_id: int) -> Optional[User]:
"""Returns User or None if not found."""
pass
def process_items(items: List[str]) -> Dict[str, int]:
"""Returns count of each item."""
pass
```
### Advanced Type Hints
```python
from typing import (
TypeVar, Generic, Protocol, Callable,
Literal, TypedDict, Final
)
# TypeVar for generics
T = TypeVar('T')
def first(items: List[T]) -> Optional[T]:
return items[0] if items else None
# Protocol for structural typing
class Renderable(Protocol):
def render(self) -> str: ...
def display(obj: Renderable) -> None:
print(obj.render())
# Literal for specific values
Status = Literal["pending", "active", "completed"]
def set_status(status: Status) -> None:
pass
# TypedDict for dictionary shapes
class UserDict(TypedDict):
id: int
name: str
email: Optional[str]
# Final for constants
MAX_SIZE: Final = 100
```
### Type Hints in Classes
```python
from dataclasses import dataclass
from typing import ClassVar, Self
@dataclass
class User:
id: int
name: str
email: str
active: bool = True
# Class variable
_instances: ClassVar[Dict[int, 'User']] = {}
def deactivate(self) -> Self:
self.active = False
return self
class Builder:
def __init__(self) -> None:
self._value: str = ""
def append(self, text: str) -> Self:
self._value += text
return self
```
## Docstrings
### Function Docstrings
```python
def calculate_discount(
price: float,
discount_percent: float,
min_price: float = 0.0
) -> float:
"""Calculate the discounted price.
Args:
price: Original price of the item.
discount_percent: Discount percentage (0-100).
min_price: Minimum price floor. Defaults to 0.0.
Returns:
The discounted price, not less than min_price.
Raises:
ValueError: If discount_percent is not between 0 and 100.
Example:
>>> calculate_discount(100.0, 20.0)
80.0
"""
if not 0 <= discount_percent <= 100:
raise ValueError("Discount must be between 0 and 100")
discounted = price * (1 - discount_percent / 100)
return max(discounted, min_price)
```
### Class Docstrings
```python
class UserService:
"""Service for managing user operations.
This service handles user CRUD operations and authentication.
It requires a database connection and optional cache.
Attributes:
db: Database connection instance.
cache: Optional cache for user lookups.
Example:
>>> service = UserService(db_connection)
>>> user = service.get_user(123)
"""
def __init__(
self,
db: DatabaseConnection,
cache: Optional[Cache] = None
) -> None:
"""Initialize the UserService.
Args:
db: Active database connection.
cache: Optional cache instance for performance.
"""
self.db = db
self.cache = cache
```
## Virtual Environments
### Setup Commands
```bash
# Create virtual environment
python -m venv .venv
# Activate (Unix/macOS)
source .venv/bin/activate
# Activate (Windows)
.venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Freeze dependencies
pip freeze > requirements.txt
```
### Modern Tools
```bash
# Using uv (recommended)
uv venv
uv pip install -r requirements.txt
# Using poetry
poetry init
poetry add requests
poetry install
# Using pipenv
pipenv install
pipenv install requests
```
### Project Structure
```
project/
├── .venv/ # Virtual environment (gitignored)
├── src/
│ └── myapp/
│ ├── __init__.py
│ ├── main.py
│ └── utils.py
├── tests/
│ ├── __init__.py
│ └── test_main.py
├── pyproject.toml # Modern project config
├── requirements.txt # Pinned dependencies
└── README.md
```
## Testing
### pytest Basics
```python
import pytest
from myapp.calculator import add, divide
def test_add_positive_numbers():
assert add(2, 3) == 5
def test_add_negative_numbers():
assert add(-1, -1) == -2
def test_divide_by_zero_raises():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
# Parametrized tests
@pytest.mark.parametrize("a,b,expected", [
(1, 1, 2),
(0, 0, 0),
(-1, 1, 0),
])
def test_add_parametrized(a, b, expected):
assert add(a, b) == expected
```
### Fixtures
```python
import pytest
from myapp.database import Database
from myapp.models import User
@pytest.fixture
def db():
"""Provide a clean database for each test."""
database = Database(":memory:")
database.create_tables()
yield database
database.close()
@pytest.fixture
def sample_user(db):
"""Create a sample user in the database."""
user = User(name="Test User", email="test@example.com")
db.save(user)
return user
def test_user_creation(db, sample_user):
found = db.find_user(sample_user.id)
assert found.name == "Test User"
```
### Mocking
```python
from unittest.mock import Mock, patch, MagicMock
import pytest
def test_api_client_with_mock():
# Create mock
mock_response = Mock()
mock_response.json.return_value = {"id": 1, "name": "Test"}
mock_response.status_code = 200
with patch('requests.get', return_value=mock_response) as mock_get:
result = fetch_user(1)
mock_get.assert_called_once_with('/users/1')
assert result['name'] == "Test"
@patch('myapp.service.external_api')
def test_with_patch_decorator(mock_api):
mock_api.get_data.return_value = {"status": "ok"}
result = process_data()
assert result["status"] == "ok"
```
## Error Handling
### Exception Patterns
```python
# Define custom exceptions
class AppError(Exception):
"""Base exception for application errors."""
pass
class ValidationError(AppError):
"""Raised when validation fails."""
def __init__(self, field: str, message: str):
self.field = field
self.message = message
super().__init__(f"{field}: {message}")
class NotFoundError(AppError):
"""Raised when a resource is not found."""
def __init__(self, resource: str, identifier: Any):
self.resource = resource
self.identifier = identifier
super().__init__(f"{resource} '{identifier}' not found")
```
### Exception Handling
```python
def get_user(user_id: int) -> User:
try:
user = db.find_user(user_id)
if user is None:
raise NotFoundError("User", user_id)
return user
except DatabaseError as e:
logger.error(f"Database error: {e}")
raise AppError("Unable to fetch user") from e
# Context managers for cleanup
from contextlib import contextmanager
@contextmanager
def database_transaction(db):
try:
yield db
db.commit()
except Exception:
db.rollback()
raise
```
## Common Patterns
### Dataclasses
```python
from dataclasses import dataclass, field
from typing import List, Optional
from datetime import datetime
@dataclass
class User:
id: int
name: str
email: str
active: bool = True
created_at: datetime = field(default_factory=datetime.now)
tags: List[str] = field(default_factory=list)
def __post_init__(self):
self.email = self.email.lower()
@dataclass(frozen=True)
class Point:
"""Immutable point."""
x: float
y: float
def distance_to(self, other: 'Point') -> float:
return ((self.x - other.x)**2 + (self.y - other.y)**2) ** 0.5
```
### Context Managers
```python
from contextlib import contextmanager
from typing import Generator
@contextmanager
def timer(name: str) -> Generator[None, None, None]:
"""Time a block of code."""
import time
start = time.perf_counter()
try:
yield
finally:
elapsed = time.perf_counter() - start
print(f"{name}: {elapsed:.3f}s")
# Usage
with timer("data processing"):
process_large_dataset()
# Class-based context manager
class DatabaseConnection:
def __init__(self, connection_string: str):
self.connection_string = connection_string
self.connection = None
def __enter__(self):
self.connection = connect(self.connection_string)
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
if self.connection:
self.connection.close()
return False # Don't suppress exceptions
```
### Decorators
```python
from functools import wraps
from typing import Callable, TypeVar, ParamSpec
import time
P = ParamSpec('P')
R = TypeVar('R')
def retry(max_attempts: int = 3, delay: float = 1.0):
"""Retry decorator with exponential backoff."""
def decorator(func: Callable[P, R]) -> Callable[P, R]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
last_exception = None
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
if attempt < max_attempts - 1:
time.sleep(delay * (2 ** attempt))
raise last_exception
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def fetch_data(url: str) -> dict:
response = requests.get(url)
response.raise_for_status()
return response.json()
```
## Code Quality Tools
### Ruff Configuration
```toml
# pyproject.toml
[tool.ruff]
line-length = 88
target-version = "py311"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # Pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
]
ignore = ["E501"] # Line too long (handled by formatter)
[tool.ruff.lint.isort]
known-first-party = ["myapp"]
```
### Type Checking with mypy
```toml
# pyproject.toml
[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_configs = true
ignore_missing_imports = true
```

View File

@@ -0,0 +1,451 @@
# TypeScript Style Guide
TypeScript-specific conventions and best practices for type-safe development.
## Strict Mode
### Enable Strict Configuration
```json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
```
### Benefits
- Catches errors at compile time
- Better IDE support and autocomplete
- Self-documenting code
- Easier refactoring
## Type Safety
### Avoid `any`
```typescript
// Bad
function processData(data: any): any {
return data.value;
}
// Good
interface DataItem {
value: string;
count: number;
}
function processData(data: DataItem): string {
return data.value;
}
```
### Use `unknown` for Unknown Types
```typescript
// When type is truly unknown
function parseJSON(json: string): unknown {
return JSON.parse(json);
}
// Then narrow with type guards
function isUser(obj: unknown): obj is User {
return (
typeof obj === "object" && obj !== null && "id" in obj && "name" in obj
);
}
```
### Prefer Explicit Types
```typescript
// Bad: Implicit any
const items = [];
// Good: Explicit type
const items: Item[] = [];
// Also good: Type inference when obvious
const count = 0; // number inferred
const name = "John"; // string inferred
```
## Interfaces vs Types
### Use Interfaces for Object Shapes
```typescript
// Preferred for objects
interface User {
id: string;
name: string;
email: string;
}
// Interfaces can be extended
interface AdminUser extends User {
permissions: string[];
}
// Interfaces can be augmented (declaration merging)
interface User {
avatar?: string;
}
```
### Use Types for Unions, Primitives, and Computed Types
```typescript
// Union types
type Status = "pending" | "active" | "completed";
// Primitive aliases
type UserId = string;
// Computed/mapped types
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// Tuple types
type Coordinate = [number, number];
```
### Decision Guide
| Use Case | Recommendation |
| ----------------------- | -------------- |
| Object shape | `interface` |
| Union type | `type` |
| Function signature | `type` |
| Class implementation | `interface` |
| Mapped/conditional type | `type` |
| Library public API | `interface` |
## Async Patterns
### Prefer async/await
```typescript
// Bad: Callback hell
function fetchUserData(id: string, callback: (user: User) => void) {
fetch(`/users/${id}`)
.then((res) => res.json())
.then((user) => callback(user));
}
// Good: async/await
async function fetchUserData(id: string): Promise<User> {
const response = await fetch(`/users/${id}`);
return response.json();
}
```
### Error Handling in Async Code
```typescript
// Explicit error handling
async function fetchUser(id: string): Promise<User> {
try {
const response = await fetch(`/users/${id}`);
if (!response.ok) {
throw new ApiError(`Failed to fetch user: ${response.status}`);
}
return response.json();
} catch (error) {
if (error instanceof ApiError) {
throw error;
}
throw new NetworkError("Network request failed", { cause: error });
}
}
```
### Promise Types
```typescript
// Return type annotation for clarity
async function loadData(): Promise<Data[]> {
// ...
}
// Use Promise.all for parallel operations
async function loadAllData(): Promise<[Users, Posts]> {
return Promise.all([fetchUsers(), fetchPosts()]);
}
```
## Module Structure
### File Organization
```
src/
├── types/ # Shared type definitions
│ ├── user.ts
│ └── api.ts
├── utils/ # Pure utility functions
│ ├── validation.ts
│ └── formatting.ts
├── services/ # Business logic
│ ├── userService.ts
│ └── authService.ts
├── components/ # UI components (if applicable)
└── index.ts # Public API exports
```
### Export Patterns
```typescript
// Named exports (preferred)
export interface User { ... }
export function createUser(data: UserInput): User { ... }
export const DEFAULT_USER: User = { ... };
// Re-exports for public API
// index.ts
export { User, createUser } from './user';
export { type Config } from './config';
// Avoid default exports (harder to refactor)
// Bad
export default class UserService { ... }
// Good
export class UserService { ... }
```
### Import Organization
```typescript
// 1. External dependencies
import { useState, useEffect } from "react";
import { z } from "zod";
// 2. Internal absolute imports
import { ApiClient } from "@/services/api";
import { User } from "@/types";
// 3. Relative imports
import { formatDate } from "./utils";
import { UserCard } from "./UserCard";
```
## Utility Types
### Built-in Utility Types
```typescript
// Partial - all properties optional
type UpdateUser = Partial<User>;
// Required - all properties required
type CompleteUser = Required<User>;
// Pick - select properties
type UserPreview = Pick<User, "id" | "name">;
// Omit - exclude properties
type UserWithoutPassword = Omit<User, "password">;
// Record - dictionary type
type UserRoles = Record<string, Role>;
// ReturnType - extract return type
type ApiResponse = ReturnType<typeof fetchData>;
// Parameters - extract parameter types
type FetchParams = Parameters<typeof fetch>;
```
### Custom Utility Types
```typescript
// Make specific properties optional
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
// Make specific properties required
type RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
// Deep readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
```
## Enums and Constants
### Prefer const Objects Over Enums
```typescript
// Enums have runtime overhead
enum Status {
Pending = "pending",
Active = "active",
}
// Prefer const objects
const Status = {
Pending: "pending",
Active: "active",
} as const;
type Status = (typeof Status)[keyof typeof Status];
```
### When to Use Enums
```typescript
// Numeric enums for bit flags
enum Permissions {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
All = Read | Write | Execute,
}
```
## Generics
### Basic Generic Usage
```typescript
// Generic function
function first<T>(items: T[]): T | undefined {
return items[0];
}
// Generic interface
interface Repository<T> {
find(id: string): Promise<T | null>;
save(item: T): Promise<T>;
delete(id: string): Promise<void>;
}
```
### Constraining Generics
```typescript
// Constrain to objects with id
function findById<T extends { id: string }>(
items: T[],
id: string,
): T | undefined {
return items.find((item) => item.id === id);
}
// Multiple constraints
function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
```
## Error Types
### Custom Error Classes
```typescript
class AppError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly statusCode: number = 500,
) {
super(message);
this.name = "AppError";
}
}
class ValidationError extends AppError {
constructor(
message: string,
public readonly field: string,
) {
super(message, "VALIDATION_ERROR", 400);
this.name = "ValidationError";
}
}
```
### Type Guards for Errors
```typescript
function isAppError(error: unknown): error is AppError {
return error instanceof AppError;
}
function handleError(error: unknown): void {
if (isAppError(error)) {
console.error(`[${error.code}] ${error.message}`);
} else if (error instanceof Error) {
console.error(`Unexpected error: ${error.message}`);
} else {
console.error("Unknown error occurred");
}
}
```
## Testing Types
### Type Testing
```typescript
// Use type assertions for compile-time checks
type Assert<T, U extends T> = U;
// Test that types work as expected
type _TestUserHasId = Assert<{ id: string }, User>;
// Expect error (compile-time check)
// @ts-expect-error - User should require id
const invalidUser: User = { name: "John" };
```
## Common Patterns
### Builder Pattern
```typescript
class QueryBuilder<T> {
private filters: Array<(item: T) => boolean> = [];
where(predicate: (item: T) => boolean): this {
this.filters.push(predicate);
return this;
}
execute(items: T[]): T[] {
return items.filter((item) => this.filters.every((filter) => filter(item)));
}
}
```
### Result Type
```typescript
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function divide(a: number, b: number): Result<number> {
if (b === 0) {
return { success: false, error: new Error("Division by zero") };
}
return { success: true, data: a / b };
}
```