Skip to content

Commit 2c3307c

Browse files
committed
docs: Add code examples and explanations to interview question answers.
1 parent 03c04f2 commit 2c3307c

File tree

1 file changed

+253
-0
lines changed

1 file changed

+253
-0
lines changed

PROJECT_INTERVIEW_QUESTIONS.md

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,61 @@ Central type definitions for Task, User, navigation params, and state shapes. En
6262
### Q9. Why separate services into database/, firebase/, sync/, and notifications/?
6363

6464
**💡 Answer:**
65+
66+
```typescript
67+
// src/services/database/realmService.ts
68+
class RealmService {
69+
private realm: Realm | null = null;
70+
async getAllTasks(userId: string): Promise<Task[]> { /* ... */ }
71+
}
72+
73+
// src/services/firebase/firebaseService.ts
74+
class FirebaseService {
75+
async signIn(email: string, password: string) { /* ... */ }
76+
}
77+
78+
// src/services/sync/syncService.ts
79+
class SyncService {
80+
async syncLocalToRemote() { /* ... */ }
81+
}
82+
83+
// src/services/notifications/notificationService.ts
84+
class NotificationService {
85+
async scheduleTaskReminder(taskId: string, title: string, timestamp: number) { /* ... */ }
86+
}
87+
```
88+
89+
**🔍 Explanation:**
90+
Makes code easier to test, debug, and modify. Changes to Firebase don't affect Realm logic.
6591
Each service has distinct responsibility: database (local storage), firebase (remote), sync (coordination), notifications (push). Follows single responsibility principle.
6692

6793
<a name="q10"></a>
6894
### Q10. Explain the custom hooks pattern (useSyncStatus, useNetworkStatus).
6995

7096
**💡 Answer:**
97+
98+
```typescript
99+
// src/hooks/useSyncStatus.ts
100+
export const useSyncStatus = () => {
101+
const { status, lastSyncedAt, error } = useSelector(
102+
(state: RootState) => state.sync
103+
);
104+
return {
105+
isSyncing: status === 'syncing',
106+
isSynced: status === 'succeeded',
107+
isError: status === 'failed',
108+
};
109+
};
110+
111+
// Usage in component
112+
const { isSyncing, isSynced } = useSyncStatus();
113+
if (isSyncing) {
114+
return <ActivityIndicator />;
115+
}
116+
```
117+
118+
**🔍 Explanation:**
119+
Reusable, testable, and hides complexity. Multiple components can use same logic.
71120
Encapsulates Redux selectors and derived state logic. Components get clean API without knowing Redux internals.
72121

73122

@@ -79,60 +128,264 @@ Encapsulates Redux selectors and derived state logic. Components get clean API w
79128
### Q11. How does the offline-first architecture work?
80129

81130
**💡 Answer:**
131+
132+
```typescript
133+
// Create task - writes to Realm immediately
134+
const task = await realmService.createTask({
135+
userId: user.uid,
136+
title: 'Buy groceries',
137+
completed: false
138+
});
139+
140+
// Sync to Firebase when online
141+
if (isConnected) {
142+
await syncService.syncLocalToRemote();
143+
}
144+
145+
// Always read from Realm (works offline)
146+
const tasks = await realmService.getAllTasks(user.uid);
147+
```
148+
149+
**🔍 Explanation:**
150+
Users can work offline. Changes queue locally and sync when connection returns.
82151
All CRUD operations write to Realm first (local), then sync to Firestore when online. App always reads from Realm.
83152

84153
<a name="q12"></a>
85154
### Q12. Explain the bidirectional sync strategy.
86155

87156
**💡 Answer:**
157+
158+
```typescript
159+
// Local to remote (syncService.ts)
160+
const unsyncedTasks = await realmService.getUnsyncedTasks(userId);
161+
for (const task of unsyncedTasks) {
162+
await firebaseService.syncTaskToFirestore(task);
163+
await realmService.markTaskAsSynced(task.id);
164+
}
165+
166+
// Remote to local (syncService.ts)
167+
firebaseService.listenToTasks(
168+
tasks => {
169+
tasks.forEach(task => realmService.saveRemoteTask(task));
170+
},
171+
error => console.error('Sync error:', error)
172+
);
173+
```
174+
175+
**🔍 Explanation:**
176+
Ensures consistency across devices. Changes from any device propagate to all others.
88177
Local-to-remote syncs unsynced tasks to Firestore. Remote-to-local listens to Firestore changes and updates Realm.
89178

90179
<a name="q13"></a>
91180
### Q13. How are sync conflicts handled?
92181

93182
**💡 Answer:**
183+
184+
```typescript
185+
// realmService.ts - saveRemoteTask
186+
async saveRemoteTask(task: Task): Promise<void> {
187+
const existingTask = this.realm.objects('Task')
188+
.filtered('id == $0', task.id)[0];
189+
190+
this.realm.write(() => {
191+
if (existingTask) {
192+
// Remote overwrites local
193+
existingTask.title = task.title;
194+
existingTask.updatedAt = task.updatedAt;
195+
existingTask.synced = true;
196+
} else {
197+
this.realm.create('Task', { ...task, synced: true });
198+
}
199+
});
200+
}
201+
```
202+
203+
**🔍 Explanation:**
204+
Simple but can lose data. Trade-off for implementation simplicity. Could enhance with CRDTs.
94205
Last-write-wins based on `updatedAt` timestamp. Remote changes always overwrite local if remote is newer.
95206

96207
<a name="q14"></a>
97208
### Q14. Why mark tasks as `synced: false` on local changes?
98209

99210
**💡 Answer:**
211+
212+
```typescript
213+
// realmService.ts - updateTask
214+
async updateTask(id: string, userId: string, updates: Partial<Task>) {
215+
this.realm.write(() => {
216+
Object.assign(task, {
217+
...updates,
218+
updatedAt: Date.now(),
219+
synced: false // Mark for sync
220+
});
221+
});
222+
}
223+
224+
// syncService.ts - syncLocalToRemote
225+
const unsyncedTasks = await realmService.getUnsyncedTasks(user.uid);
226+
for (const task of unsyncedTasks) {
227+
await firebaseService.syncTaskToFirestore(task);
228+
}
229+
```
230+
231+
**🔍 Explanation:**
232+
Efficient sync - only uploads changed tasks, not entire database.
100233
Tracks which tasks need uploading to Firestore. Sync service queries unsynced tasks to upload.
101234

102235
<a name="q15"></a>
103236
### Q15. Explain soft delete vs hard delete implementation.
104237

105238
**💡 Answer:**
239+
240+
```typescript
241+
// Soft delete - syncs to Firebase
242+
async deleteTask(id: string, userId: string) {
243+
this.realm.write(() => {
244+
task.isDeleted = true;
245+
task.synced = false; // Will sync deletion
246+
task.updatedAt = Date.now();
247+
});
248+
}
249+
250+
// Hard delete - after sync completes
251+
async permanentlyDeleteTask(id: string, userId: string) {
252+
this.realm.write(() => {
253+
this.realm.delete(task);
254+
});
255+
}
256+
```
257+
258+
**🔍 Explanation:**
259+
Soft delete ensures deletions sync to Firestore before permanent removal.
106260
Soft delete marks `isDeleted: true`, hard delete removes from database. Soft delete allows sync of deletions.
107261

108262
<a name="q16"></a>
109263
### Q16. How does the filter state work in TaskListScreen?
110264

111265
**💡 Answer:**
266+
267+
```typescript
268+
// TaskListScreen.tsx
269+
const { tasks, filter } = useAppSelector(state => state.tasks);
270+
271+
const filteredTasks = useMemo(() => {
272+
return tasks.filter(task => {
273+
if (filter === 'active') return !task.completed;
274+
if (filter === 'completed') return task.completed;
275+
return true;
276+
});
277+
}, [tasks, filter]);
278+
```
279+
280+
**🔍 Explanation:**
281+
Memoization prevents re-filtering on every render. Only recalculates when tasks or filter change.
112282
Redux stores filter ('all'|'active'|'completed'), useMemo filters tasks client-side for performance.
113283

114284
<a name="q17"></a>
115285
### Q17. Why use Redux Toolkit instead of plain Redux?
116286

117287
**💡 Answer:**
288+
289+
```typescript
290+
// tasksSlice.ts
291+
const tasksSlice = createSlice({
292+
name: 'tasks',
293+
initialState,
294+
reducers: {
295+
setTasks: (state, action) => {
296+
state.tasks = action.payload; // Immer makes this safe
297+
},
298+
toggleTaskComplete: (state, action) => {
299+
const task = state.tasks.find(t => t.id === action.payload);
300+
if (task) {
301+
task.completed = !task.completed; // Direct mutation works!
302+
}
303+
}
304+
}
305+
});
306+
```
307+
308+
**🔍 Explanation:**
309+
Less code, fewer bugs, better DX. Immer handles immutability automatically.
118310
RTK provides `createSlice` (reduces boilerplate), Immer (immutable updates), and configured store with DevTools.
119311

120312
<a name="q18"></a>
121313
### Q18. Explain the serializableCheck middleware configuration.
122314

123315
**💡 Answer:**
316+
317+
```typescript
318+
// store/index.ts
319+
export const store = configureStore({
320+
reducer: { auth: authReducer, tasks: tasksReducer },
321+
middleware: getDefaultMiddleware =>
322+
getDefaultMiddleware({
323+
serializableCheck: {
324+
ignoredPaths: ['auth.user'],
325+
ignoredActions: ['auth/setUser'],
326+
},
327+
}),
328+
});
329+
```
330+
331+
**🔍 Explanation:**
332+
Prevents console warnings while storing Firebase User. Trade-off: can't time-travel debug user object.
124333
Redux requires serializable state, but Firebase User objects aren't serializable. Config ignores specific paths.
125334

126335
<a name="q19"></a>
127336
### Q19. How does Realm change listener trigger UI updates?
128337

129338
**💡 Answer:**
339+
340+
```typescript
341+
// syncService.ts - initialize
342+
this.unsubscribeRealm = await realmService.addChangeListener(() => {
343+
if (this.isConnected) {
344+
this.syncLocalToRemote();
345+
}
346+
});
347+
348+
// realmService.ts - addChangeListener
349+
async addChangeListener(callback: () => void) {
350+
const tasks = this.realm.objects('Task');
351+
const listener = () => callback();
352+
tasks.addListener(listener);
353+
return () => tasks.removeListener(listener);
354+
}
355+
```
356+
357+
**🔍 Explanation:**
358+
Reactive data flow: Realm change → sync → Redux update → UI re-render.
130359
Realm listener calls callback on data changes, which loads tasks from Realm and dispatches to Redux, triggering re-render.
131360

132361
<a name="q20"></a>
133362
### Q20. Why separate network state into its own Redux slice?
134363

135364
**💡 Answer:**
365+
366+
```typescript
367+
// networkSlice.ts
368+
interface NetworkState {
369+
isConnected: boolean;
370+
type: string | null;
371+
isInternetReachable: boolean | null;
372+
}
373+
374+
// NetworkProvider.tsx
375+
NetInfo.addEventListener(state => {
376+
dispatch(setNetworkState({
377+
isConnected: state.isConnected ?? false,
378+
type: state.type,
379+
isInternetReachable: state.isInternetReachable ?? null
380+
}));
381+
});
382+
383+
// Usage in components
384+
const { isConnected } = useAppSelector(state => state.network);
385+
```
386+
387+
**🔍 Explanation:**
388+
Single source of truth. Components react to network changes without managing listeners.
136389
Network status affects multiple features (sync, offline banner). Centralized state prevents duplicate listeners.
137390

138391

0 commit comments

Comments
 (0)