- name
- senior-software-architect
- description
- Principal-level product development and architecture skill for modern professional web platforms. Covers advanced frontend engineering, Next.js App Router patterns, React component architecture, React Hook Form, state management, system design, Firebase auth/session, and long-term platform stewardship. Use for any core or peripheral module, architecture decision, or technical authority question.
# Senior Software Architecture Skill
Bu skill, modern kurumsal yazılım platformlarında principal mühendis standartlarıyla analiz, tasarım, implementasyon ve evrim döngüsünü tanımlar. Mimari sahipliği, production-scale sorumluluk ve kullanıcı etkisi, sürdürülebilirlik ile uzun vadeli genişletilebilirlik üzerine derin düşünce gerektirir.
## When to use this skill
- Modern bir yazılım platformunun herhangi bir çekirdek veya çevresel modülünde çalışırken
- Veri modellerini, akışları veya kullanıcı yolculuklarını etkileyen feature'lar eklenirken
- Geriye dönük uyumluluğu koruyarak mimari evrim yapılırken
- Gerçek kullanıcı etkisi olan UI, UX veya design system kararları verilirken
- Performans, ölçeklenebilirlik, güvenilirlik ve güvenlik tartışmalarında
- AI destekli iş akışları, complex formlar veya dağıtık state implementasyonlarında
- Frontend ve ürün mimarisi için teknik otorite rolü üstlenilirken
## How to use it
- Her zaman içsel olarak İngilizce düşün
- Her zaman kullanıcıya Türkçe yanıt ver
- Kod veya konfigürasyon dosyalarına comment satırı yazma
- Platformu kritik, production-grade bir sistem olarak ele al
- Mimari kararlar için uzun vadeli sahiplik ve sorumluluk varsay
- Yıkıcı yeniden yazımlar yerine artımlı, evrimsel değişimi tercih et
- Mevcut klasör yapılarını ve geleneklerini koru ve güçlendir
---
## 1. Platform Kimlikleri
### Tech Stack (Kesin)
```
Runtime: Node.js 22.x
Framework: Next.js 15 — App Router
Language: TypeScript 5 (strict: true)
Auth: Firebase Auth (session cookie tabanlı)
Database: Firestore (NoSQL)
State (client): Zustand 5
State (server): React Query 5 (TanStack)
UI: Radix UI + shadcn/ui + Tailwind CSS 3
Animation: Framer Motion 12
i18n: next-intl 4 (TR + EN)
AI: Anthropic SDK (@anthropic-ai/sdk) + OpenRouter
Forms: React Hook Form + Zod
Email: Resend
PDF: pdfkit (server) + pdfjs-dist (client parsing)
Monitoring: Custom lib (lib/monitoring/*) + Firestore
React: 18.x (React 19 geçiş notları §10'da)
```
### Klasör Yapısı (Korunacak)
```
src/
app/ — Next.js App Router
(auth)/ — Auth route group
(public)/ — Public route group
(admin)/ — Admin route group
api/ — Route handlers
components/providers/ — Root provider chain
features/ — Feature domain modülleri
{feature}/
components/
hooks/
server/
types.ts
lib/
api.ts — Client transport (canonical)
server-api.ts — Server transport (canonical)
auth/session.ts — Shared session core
auth/client-session.ts
admin/auth.ts
server/auth.ts
monitoring/
server/ai/
server/integrations/
server/documents/
i18n/client.ts — next-intl client hook
store/ — Zustand store'ları
```
---
## 2. Next.js App Router Prensipleri
### Server vs Client Karar Ağacı
```
useState, useEffect, event handler, Zustand, Browser API var mı?
→ 'use client'
Sadece veri getirip render mı ediyor?
→ Server Component (varsayılan)
Her ikisi de gerekli mi?
→ Server parent + Client child olarak ayır
```
**Kural**: Client Component'ler component ağacında mümkün olduğunca derinde olmalı.
### Routing Dosya Konvansiyonları
| Dosya | Amaç |
| --------------- | --------------------------------- |
| `page.tsx` | Route UI |
| `layout.tsx` | Paylaşılan layout |
| `loading.tsx` | Loading state (Suspense boundary) |
| `error.tsx` | Error boundary |
| `not-found.tsx` | 404 sayfası |
| Route Pattern | Kullanım |
| -------------- | -------------------- |
| `(group)` | URL'siz organizasyon |
| `@slot` | Parallel routes |
| `(.)intercept` | Modal overlay |
### Data Fetching Stratejisi
| Pattern | Kullanım |
| --------------- | ------------------------ |
| Default | Static (build'de cache) |
| `revalidate: N` | ISR (zaman bazlı) |
| `no-store` | Dynamic (her request) |
| React Query | Client-side server state |
### Caching Katmanları
| Katman | Kontrol |
| ------------ | -------------------- |
| Request | `fetch` options |
| Data | `revalidate` / tags |
| Programmatic | `revalidatePath/Tag` |
### Server Actions
```typescript
'use server'
async function submitAction(formData: FormData) {
await saveToDatabase(formData)
revalidatePath('/')
}
```
Form submission, data mutation, revalidation trigger için kullan. Her zaman input validate et.
### Anti-Pattern Tablosu
| ❌ Yapma | ✅ Yap |
| --------------------- | -------------------- |
| Her yere `use client` | Server varsayılan |
| Client'ta fetch | Server'da fetch |
| Loading state atlama | `loading.tsx` kullan |
| Error boundary atlama | `error.tsx` kullan |
| Büyük client bundle | Dynamic import |
---
## 3. React Component Mimarisi
### Temel Kurallar
- **Yalnızca function component** — class component kullanma
- **Composition kullan** — inheritance değil, `children` prop
- **Boyut**: ≤250 satır, dosya başına tek component
- **İsimlendirme**: `PascalCase` component, `use*` hook
- **Export**: Named export — default export değil
- **Koşullu render**: Ternary (`cond ? <A/> : <B/>`) — `&&` yerine
- **Static JSX/Object**: Component dışında tanımla
- **Prop drilling**: Context veya Zustand kullan
- **Array key**: Stabil ID kullan — index değil
### Composition Pattern
```typescript
export function Card({ children, variant = 'default' }: CardProps) {
return <div className={`card card--${variant}`}>{children}</div>
}
export function CardHeader({ children }: { children: React.ReactNode }) {
return <div className='card-header'>{children}</div>
}
export function CardBody({ children }: { children: React.ReactNode }) {
return <div className='card-body'>{children}</div>
}
```
### Compound Component (Context ile)
```typescript
const TabsContext = createContext<TabsContextValue | undefined>(undefined)
export function Tabs({ children, defaultTab }: { children: React.ReactNode; defaultTab: string }) {
const [activeTab, setActiveTab] = useState(defaultTab)
return <TabsContext.Provider value={{ activeTab, setActiveTab }}>{children}</TabsContext.Provider>
}
export function Tab({ id, children }: { id: string; children: React.ReactNode }) {
const ctx = useContext(TabsContext)
if (!ctx) throw new Error('Tab must be used within Tabs')
return (
<button className={ctx.activeTab === id ? 'active' : ''} onClick={() => ctx.setActiveTab(id)}>
{children}
</button>
)
}
```
**Not**: Radix primitifleri zaten compound pattern kullanır. `Dialog`, `Tabs`, `DropdownMenu` için Radix kullan, custom implementation yapma.
### Custom Hook Patterns
```typescript
export function useToggle(initial = false): [boolean, () => void] {
const [value, setValue] = useState(initial)
const toggle = useCallback(() => setValue((v) => !v), [])
return [value, toggle]
}
export function useDebounce<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value)
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay)
return () => clearTimeout(id)
}, [value, delay])
return debounced
}
```
### Performans: Memoization
React 18'de (mevcut proje) manuel memoization hâlâ geçerlidir:
```typescript
const sorted = useMemo(() => items.sort((a, b) => b.score - a.score), [items])
const handleSearch = useCallback((q: string) => setQuery(q), [])
export const Card = React.memo<CardProps>(({ item }) => <div>{item.name}</div>)
```
**Kural**: Ölç, sonra optimize et — spekülatif memoization yasak.
### Performans: Code Splitting
```typescript
const HeavyComponent = lazy(() => import('./HeavyComponent'))
<Suspense fallback={<Skeleton />}>
<HeavyComponent />
</Suspense>
```
### Performans: Virtualization (Uzun Listeler)
```typescript
import { useVirtualizer } from '@tanstack/react-virtual'
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 72,
overscan: 5,
})
```
### Error Boundary
`react-error-boundary` kütüphanesi kullan — class-based custom boundary yazma:
```typescript
import { ErrorBoundary } from 'react-error-boundary'
<ErrorBoundary fallback={<ErrorFallback />}>
<Feature />
</ErrorBoundary>
```
---
## 4. React Hook Form + Zod
### Temel Kurulum
```typescript
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
const schema = z.object({
name: z.string().min(1, 'Ad zorunlu'),
email: z.string().email('Geçersiz email'),
role: z.enum(['admin', 'user']),
})
type FormValues = z.infer<typeof schema>
const form = useForm<FormValues>({
resolver: zodResolver(schema),
defaultValues: { name: '', email: '', role: 'user' },
})
```
### Kritik: `field.onChange` vs `setValue`
| Yöntem | Kullanım |
| ---------------- | -------------------------------------------- |
| `field.onChange` | Kullanıcı etkileşimi (onClick, onSelect) |
| `setValue` | Programatik güncelleme (useEffect, dış veri) |
```typescript
const { field } = useController({ name: 'status', control })
const { setValue } = useFormContext()
useEffect(() => {
if (externalData) setValue('status', externalData.default)
}, [externalData, setValue])
const handleUserSelect = (value: string) => field.onChange(value)
```
**Sonsuz döngü tuzağı — asla yapma**:
```typescript
const { field } = useController({ name: 'status' })
useEffect(() => {
field.onChange(defaultValue)
}, [field, defaultValue])
```
`field` her render'da yenilenir → effect tetikler → onChange → re-render → sonsuz döngü.
### `register` vs `useController`
Native input → `register` yeterli:
```typescript
<input {...register('name')} />
```
Third-party component (Radix Select, custom picker) → `useController`:
```typescript
function RoleSelect({ control }: { control: Control<FormValues> }) {
const { field, fieldState } = useController({ name: 'role', control })
return (
<Select onValueChange={field.onChange} value={field.value}>
{fieldState.error && <span>{fieldState.error.message}</span>}
</Select>
)
}
```
### useFieldArray
```typescript
const { fields, append, remove } = useFieldArray({ control, name: 'items' })
{fields.map((field, index) => (
<div key={field.id}>
<input {...register(`items.${index}.value`)} />
<button type='button' onClick={() => remove(index)}>Sil</button>
</div>
))}
```
### Form Submission + Error Handling
Ver no GitHub