Sparround

Live coding və code review ssenariləri

Live coding mərhələsində məqsəd işləyən kod deyil — düşüncə prosesini səsli göstərməkdir. İşləyən struktur:

1. Tələbi dəqiqləşdir (30 saniyə): "Bu ekran offline işləməlidir? Yükləmə zamanı köhnə məlumat qalmalıdır? Səhifələmə var?" 2. Modeli əvvəlcə yaz: state sinfi və onun mümkün vəziyyətləri. Bu, müsahibə "state modelləşdirməni bilir" siqnalı verir. 3. UI-ı sonra: state-dən UI-a keçid switch/pattern matching ilə. 4. Xəta və boş vəziyyəti unutma — namizədlərin çoxu yalnız uğurlu yolu yazır; bu, ən tez görünən fərqdir. 5. Testi ən azı ağızda de: "Bu notifier üçün ilk testim boş siyahı halı olardı." 6. Trade-off-u səsləndir: "Burada optimistik yenilənmə seçdim, çünki əməliyyat geri qaytarıla biləndir."

dart
// 1) Model: mümkün olmayan vəziyyətlər ifadə edilə bilməz.
// AsyncValue loading/error/data-nı özü daşıyır, ona görə əlavə model lazım deyil.

// 2) State qatı.
class Articles extends AsyncNotifier<List<Article>> {
  @override
  Future<List<Article>> build() {
    return ref.watch(articleRepositoryProvider).fetchAll();
  }

  Future<void> refresh() async {
    state = const AsyncLoading<List<Article>>().copyWithPrevious(state);
    state = await AsyncValue.guard(
      () => ref.read(articleRepositoryProvider).fetchAll(),
    );
  }
}

final articlesProvider =
    AsyncNotifierProvider<Articles, List<Article>>(Articles.new);

// 3) UI: dörd hal — data, boş, xəta, ilk yükləmə.
class ArticlesPage extends ConsumerWidget {
  const ArticlesPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final articles = ref.watch(articlesProvider);

    return Scaffold(
      body: RefreshIndicator(
        onRefresh: () => ref.refresh(articlesProvider.future),
        child: switch (articles) {
          AsyncValue(:final value?) when value.isEmpty =>
            const EmptyView(message: 'Hələ məqalə yoxdur'),
          AsyncValue(:final value?) => ArticleListView(items: value),
          AsyncValue(:final error?) => ErrorView(
              message: '$error',
              onRetry: () => ref.invalidate(articlesProvider),
            ),
          _ => const ArticleSkeleton(),
        },
      ),
    );
  }
}

Tipik tapşırıq: siyahı + yükləmə + xəta + "yenidən yoxla". Riverpod variantı.

dart
enum ArticlesStatus { initial, loading, success, failure }

final class ArticlesState extends Equatable {
  const ArticlesState({
    this.status = ArticlesStatus.initial,
    this.items = const [],
    this.error,
  });

  final ArticlesStatus status;
  final List<Article> items;
  final String? error;

  ArticlesState copyWith({
    ArticlesStatus? status,
    List<Article>? items,
    String? error,
  }) => ArticlesState(
        status: status ?? this.status,
        items: items ?? this.items,
        error: error,
      );

  @override
  List<Object?> get props => [status, items, error];
}

sealed class ArticlesEvent {}
final class ArticlesStarted extends ArticlesEvent {}
final class ArticlesRefreshRequested extends ArticlesEvent {}

class ArticlesBloc extends Bloc<ArticlesEvent, ArticlesState> {
  ArticlesBloc(this._repository) : super(const ArticlesState()) {
    on<ArticlesStarted>(_onLoad);
    // Təkrar sorğuların qarşısını alır.
    on<ArticlesRefreshRequested>(_onLoad, transformer: droppable());
  }

  final ArticleRepository _repository;

  Future<void> _onLoad(ArticlesEvent event, Emitter<ArticlesState> emit) async {
    emit(state.copyWith(status: ArticlesStatus.loading, error: null));
    try {
      final items = await _repository.fetchAll();
      emit(state.copyWith(status: ArticlesStatus.success, items: items));
    } catch (error) {
      // Köhnə items saxlanılır: ekran boşalmır.
      emit(state.copyWith(status: ArticlesStatus.failure, error: '$error'));
    }
  }
}

Eyni tapşırıq, BLoC variantı — status enum ilə.

Code review-da red flagNiyə problem
`build` metodunda `Navigator`, `showDialog`, `showSnackBar`Rebuild təkrarlandıqda effekt təkrarlanır
`build`-də `read`/`context.read<T>().state` ilə dəyər oxumaqAbunəlik yoxdur — UI köhnə dəyərlə qalır (sənəd bunu "səhvə meyllidir" adlandırır)
State-in kolleksiyasını yerində dəyişmək (`items.add(...)`)`==` fərq görmür: bloc-da dəyişiklik udulur, `select` filtri işləmir
`Equatable`-da `props`-a yeni sahənin əlavə edilməməsiİki fərqli state bərabər sayılır — səssiz bug
Notifier/provider-in `build` metodunda yaradılmasıHər rebuild-də yeni obyekt: state itir, köhnəsi sızır
`await`-dan sonra `mounted` yoxlanışı olmadan `context`Söküləmiş widget-də crash; lint qaydası bunu tutur
Ephemeral state (form, scroll) üçün qlobal providerGeniş rebuild və route-lar arası state sızması; Riverpod sənədi buna qarşı xəbərdarlıq edir
Kritik axında transformer-in göstərilməməsiİki dəfə basılış → iki sifariş/ödəniş

Live coding-də ən çox itirilən bal — xəta və boş vəziyyətin yazılmamasıdır. Yalnız uğurlu yolu yazan namizədlə dörd halı (ilk yükləmə, boş, xəta, yenidən yükləmə) nəzərə alan namizəd arasındaki fərq kod keyfiyyətindən çox məhsul düşüncəsi kimi qiymətləndirilir. Bir cümlə kifayətdir: "Boş siyahı və xəta ekranı da lazımdır — onları da əlavə edirəm."

📚 Mənbələr və sənədlər