Trimester 2 Retrospective

Personal Contribution Analytics : Shriya

This document summarizes measurable development contributions made throughout the project.

  • Commits: 65 commits to Spring and Pages in the 2 months
  • Pull Requests: 9 for Spring and Pages
  • Issues: 32 issues created for planning and organization


Issues

Records of created, managed, and resolved issues within the repository.


Commit Activity

Steady commit history reflecting ongoing development work across the project timeline.

What this indicates

  • Consistent engagement in coding tasks
  • Continuous improvements rather than last-minute uploads
  • Active participation throughout different phases of development

Pull Requests

Pull requests opened, reviewed, and successfully merged into the main branch.

What this indicates

  • Ownership of feature implementation
  • Meaningful contributions to the shared codebase
  • Collaboration through structured review and merge workflows

Contributions to Pages

Documented updates and commits related to project documentation or GitHub Pages.


What this indicates

  • Sustained involvement in project progress
  • Ongoing responsibility in development and maintenance

Overall Summary

These analytics highlight consistent technical contribution, collaborative engagement, and accountability throughout the coding, reviewing, and planning stages of the project lifecycle.

Dynamic Rate Limiting — Adaptive API Protection in Spring Boot

Overview

This trimester, I implemented a dynamic rate limiting system in the Spring Boot backend to protect APIs from abuse while maintaining fairness and scalability under varying load conditions.

Rather than using a static per-user request limit, I designed a system that:

  • Tracks active authenticated users
  • Dynamically adjusts request limits based on system load
  • Rebuilds user-specific token buckets when limits change
  • Uses Bucket4j for in-memory token bucket enforcement
  • Prevents abuse while scaling gracefully

The result is an adaptive rate limiter that balances protection and performance.


Part 1 — The Problem

APIs are vulnerable to:

  • Brute-force login attempts
  • API scraping
  • Automated abuse
  • Resource exhaustion under high load

A static rate limit (e.g., 20 requests per minute) has drawbacks:

  • Too restrictive when few users are active
  • Too permissive during high traffic
  • Does not scale with system load

We needed a smarter system that adjusts limits dynamically.


Part 2 — The Solution

I implemented a RateLimitFilter using Spring’s OncePerRequestFilter and Bucket4j.

@Component
public class RateLimitFilter extends OncePerRequestFilter {

This filter:

  • Executes once per HTTP request
  • Identifies the user (or IP if unauthenticated)
  • Computes a dynamic rate limit
  • Applies token bucket enforcement
  • Returns HTTP 429 when the limit is exceeded

Part 3 — Core Design

1. User Identification

Each request is mapped to a unique key:

String username = request.getUserPrincipal() != null
        ? request.getUserPrincipal().getName()
        : request.getRemoteAddr();

This ensures:

  • Authenticated users are rate limited individually
  • Unauthenticated users are rate limited by IP

2. Active User Tracking

Authenticated users are tracked:

if (request.getUserPrincipal() != null) {
    activeUsers.put(username, true);
}

This allows the system to compute limits based on real-time user load.


3. Dynamic Limit Calculation

Instead of a fixed limit, the system adjusts based on active users:

private int computeDynamicLimit(String username, int activeUsers) {
    if (activeUsers < 5) {
        return 100;
    } else if (activeUsers < 20) {
        return 300;
    } else {
        return 600;
    }
}

Logic Breakdown

Active Users Requests Per Minute
< 5 100
< 20 300
20+ 600

This ensures:

  • Generous limits when few users are online
  • Higher aggregate throughput when system usage grows
  • Adaptive scaling behavior

Part 4 — Token Bucket Implementation (Bucket4j)

Each user has their own bucket stored in a concurrent cache:

private final Map<String, Bucket> cache = new ConcurrentHashMap<>();
private final Map<String, Integer> userLimits = new ConcurrentHashMap<>();

When a limit changes, the bucket is rebuilt:

cache.put(username, createBucketWithLimit(dynamicLimit));
userLimits.put(username, dynamicLimit);

Bucket Creation

private Bucket createBucketWithLimit(int limit) {
    Bandwidth bandwidth = Bandwidth.builder()
            .capacity(limit)
            .refillGreedy(limit, Duration.ofMinutes(1))
            .build();

    return Bucket.builder()
            .addLimit(bandwidth)
            .build();
}

How This Works

  • capacity(limit) → Maximum tokens available
  • refillGreedy(limit, Duration.ofMinutes(1)) → Refill all tokens every minute
  • tryConsume(1) → Consumes 1 token per request

If no tokens remain, the request is rejected.


Part 5 — Request Flow

For every request:

  1. Identify user or IP
  2. Track active authenticated users
  3. Compute dynamic limit
  4. Rebuild bucket if limit changed
  5. Attempt to consume 1 token
  6. If allowed → continue request
  7. If denied → return HTTP 429
if (bucket.tryConsume(1)) {
    filterChain.doFilter(request, response);
} else {
    response.setStatus(429);
    response.getWriter().write("Too many requests - limit: " + dynamicLimit + " per minute");
}

Part 6 — Why This Design Works

Thread-Safe

Uses ConcurrentHashMap for safe concurrent access.

Per-User Isolation

Each user/IP has their own independent bucket.

Dynamic Adaptation

Limits adjust automatically as user load changes.

Minimal Overhead

In-memory token buckets are lightweight and fast.

Configurable Defaults

Default limit can be injected via:

@Value("${security.rate-limit.requests-per-minute:20}")

This allows environment-based configuration.


Part 7 — Security Impact

This rate limiter protects against:

  • Brute force attacks
  • API scraping
  • Request flooding
  • Denial-of-service style abuse

And it does so without punishing legitimate users during low-traffic periods.


Future Improvements

Near-Term

  • Add sliding window tracking instead of greedy refill
  • Track burst detection patterns
  • Add logging for rate-limit violations
  • Include Retry-After header

Longer-Term

  • Redis-backed distributed rate limiting
  • Separate limits per endpoint
  • Role-based rate limits (admin vs student)
  • Integration with anomaly detection system
  • Dashboard for monitoring rate-limit metrics

Reflection

This project reinforced an important engineering principle:

Security controls should scale with system behavior.

A fixed rate limit is simple, but adaptive rate limiting is resilient.

By combining:

  • Spring Security
  • Bucket4j token buckets
  • Real-time active user tracking

I built a rate limiting system that protects APIs while preserving flexibility.

It is not just a throttle — it is an adaptive protection layer.

FRQ Unification : Standardizing AP CSA Free Response Across the Platform

Overview

This trimester, my work focused on eliminating fragmentation in how AP CSA Free Response Questions (FRQs) were authored, displayed, submitted, graded, and reviewed across the Open Coding Society platform.

Previously, FRQs were implemented as question-specific features. Each year or problem often had custom frontend layouts and inconsistent backend submission logic. This slowed development, created duplication, and made automation difficult.

The solution was to design and implement a fully unified FRQ system built around:

  • One reusable frontend layout
  • A centralized backend submission pipeline
  • Automated AI rubric grading
  • Structured, persistent feedback storage
  • Seamless integration with existing assignment and grading systems

This transformed FRQs from isolated pages into a standardized, extensible framework.


Part 1 — FRQ Fragmentation

The Problem

FRQs were previously implemented with:

  • Question-specific layouts
  • Hardcoded rendering logic
  • Inconsistent submission handling
  • Disconnected grading workflows

Adding a new FRQ required touching multiple frontend and backend files.

This created:

  • Duplicate layout code
  • Increased maintenance cost
  • Risk of inconsistent student experience
  • Difficulty integrating AI grading cleanly

The system lacked a single source of truth for FRQs.


The Fix — Unified FRQ Layout

I introduced a single reusable layout:

_layouts/frq.html

All FRQs now can use metadata-driven front matter:

---
layout: frq
title: 2025 FRQ 2
year: 2025
frq_number: 2
unit: Arrays
category: Methods and Control Flow
---

The layout dynamically renders:

  • Question metadata
  • Prompt content
  • Submission textarea
  • Submission + refresh logic

Why This Works

  • No hardcoded question IDs
  • No duplicated layout logic
  • All FRQs share the same rendering engine
  • Adding new FRQs requires only metadata

This shifted FRQs from “custom pages” to structured content.


Part 2 — Unified Submission & Grading Pipeline

The Problem

Before unification:

  • Submission handling varied across FRQs
  • Grading logic was not centralized
  • AI feedback lacked consistent structure
  • Submissions were not fully aligned with assignment systems

This prevented clean automation.


The Fix — Centralized Submission Flow

All FRQs now can follow a single submission pipeline.

Step 1 — Save Submission

POST /api/submissions/submit/{assignmentId}

Creates a new AssignmentSubmission record.

Step 2 — Trigger AI Grading

POST /api/frq-feedback/submission/{submissionId}

This:

  • Sends student response to Gemini
  • Applies AP-style rubric evaluation
  • Generates structured feedback
  • Persists results to database

Step 3 — Retrieve Updated Results

GET /api/submissions/getSubmissions/{studentId}

Allows frontend to refresh and display:

  • Score
  • Strengths
  • Improvements
  • Overall comments

Authentication

All requests use:

credentials: "include"

This ensures:

  • Session-based authentication
  • No hardcoded student IDs
  • Secure mapping between users and submissions

Part 3 — Structured Feedback & Persistence

The Problem

AI grading output must be:

  • Structured
  • Persistable
  • Queryable
  • Transparent
  • Future-proof

Storing raw text responses would limit analytics and extensibility.


The Fix — Structured Feedback Object

Feedback is stored in a normalized format inside the feedback table.

assignment_submission Table

Stores:

  • Student response
  • Normalized numeric score
  • Feedback summary
  • Timestamps

This acts as the canonical grading record.

feedback Table

Stores:

  • Score breakdown
  • Strengths
  • Areas for improvement
  • Overall feedback
  • Raw AI response
  • Grading metadata

Example Feedback Payload

{
  "question_id": "2025-FRQ-2",
  "score": 7,
  "max_score": 9,
  "strengths": [
    "Correct use of instance variables",
    "Clear method structure"
  ],
  "improvements": [
    "Missing edge-case handling",
    "Loop condition could be simplified"
  ],
  "overall_feedback": "Strong implementation with minor logical gaps.",
  "graded_at": "2026-01-24T10:12:00Z",
  "graded_by": "Gemini AI"
}

Why This Matters

  • Enables cross-FRQ analytics
  • Supports rubric adjustments without schema churn
  • Allows dashboards and exports
  • Improves transparency for students
  • Preserves raw output for debugging

Integration With Existing Systems

A core principle was unification, not duplication.

The FRQ system integrates directly with:

1. AssignmentSubmission Framework

FRQs are treated as standard assignments.

This enables:

  • Individual grading
  • Group submissions
  • Grade back-propagation
  • Dashboard integration

No special-case logic required.

2. Grade API

Because FRQs persist as AssignmentSubmission records:

  • Scores appear in grade dashboards
  • Exports work automatically
  • Analytics remain consistent

3. Gemini AI Feedback Service

The unified layout connects directly to the Gemini grading service.

The end-to-end flow:

Frontend → Submission API → Gemini → Structured Feedback → Persistence → Display

This ensures consistency across all FRQs.


Relevant PRs & Commits

Commit / PR Description
aa304e6 Create unified FRQ layout
5d73750 Integrate FRQ with AssignmentSubmission system
` 8286e24` Connect Gemini grading pipeline
c68416e Normalize structured feedback storage

Impact & Benefits

Consistency

All FRQs now can follow a single rendering and grading workflow.

Scalability

Supports all AP CSA FRQs across years using one layout.

New FRQs require:

  • Front matter metadata
  • Prompt content

No backend changes.

Automation

Manual grading is replaced with rubric-aligned AI evaluation.

Maintainability

Fragmented code paths eliminated. Future enhancements require changes in one place.


Future Improvements

Near-Term

  • Rubric calibration improvements
  • AI confidence scoring
  • Cross-FRQ performance analytics
  • Response similarity detection

Longer-Term

  • Instructor override interface
  • Rubric version tracking
  • Comparative cohort analytics
  • AI drift monitoring
  • Real-time grading performance dashboards

Reflection

The biggest lesson this trimester was that fragmentation quietly compounds technical debt.

The database reliability project taught me that reactive systems fail under pressure.
The FRQ Unification project reinforced that proactive system design prevents fragmentation before it spreads.

By consolidating FRQs into a single reusable pipeline:

  • Developer velocity increased
  • Student experience became consistent
  • AI grading became reliable
  • Maintenance complexity dropped

What was previously a collection of isolated features is now a standardized grading framework.

Instead of building individual FRQs, we built an infrastructure.

And infrastructure scales.

Computer Science Growth : Before vs After the Trimester

Overview

This trimester marked a major shift in how I approach computer science. The change wasn’t about working harder — I was already putting in significant effort — but about working with more structure, focus, and long-term system awareness.


Before the Trimester

I was already highly productive and deeply involved in development. I built complex backend features, worked across multiple repositories, implemented new functionality quickly, and handled bugs and integration issues independently. I consistently pushed commits and contributed to architecture discussions — comfortable jumping into new challenges and moving fast.

However, my workflow often looked like this: build Feature A, notice an improvement opportunity in Feature B, switch to Feature B, start Feature C, return to Feature A later. I was doing a lot and learning rapidly, but sometimes splitting attention across multiple parallel improvements.

My mindset was: “Keep building. Keep improving. Keep moving.”

That energy was a strength — but it needed refinement.


After the Trimester

This trimester helped me refine that energy into discipline. Working on larger systems like FRQ Unification, database recovery and migration pipelines, dynamic rate limiting, and automated database protection required sustained focus and architectural consistency.

Now I design features fully before implementing, finish one feature end-to-end before moving on, prioritize integration and testing earlier, think about long-term maintenance while coding, and build reusable infrastructure instead of isolated features.

Instead of simply asking “What can I improve next?” I now ask “What is the most strategically important thing to complete right now?”


What Actually Changed

1. Focus Shift — From Parallel to Sequential Depth

Before, I worked on multiple improvements simultaneously. Now I complete features with full lifecycle discipline: Design → Implement → Test → Integrate → Document → Move On. The workload stayed high — but the execution became more deliberate.

2. From Feature Builder to System Thinker

Before, I was strong at implementing functionality quickly. Now I’m strong at designing systems that unify and scale. Converting fragmented FRQs into a unified grading framework, designing adaptive rate limiting instead of static throttling, and building automated recovery pipelines instead of manual fixes — the shift wasn’t about capability, it was about architectural maturity.

3. From Reactive Improvement to Proactive Design

Before: see problem → fix problem. Now: anticipate problem → design protection → automate resilience. This trimester strengthened my ability to think several layers ahead — what breaks under scale, what happens under failure, how does this integrate long-term, can this be reused instead of rewritten?


Biggest Growth

I didn’t become more hardworking — I became more intentional. I moved from a high-output developer to a high-output systems designer. The energy and drive were always there. What improved was focus, sequencing, and architectural depth.


Final Reflection

This trimester refined how I build.

I was already building a lot. Now I build with structure. I was already solving problems. Now I design systems that prevent them.

The growth wasn’t in effort — it was in engineering maturity. And that shift has elevated both the quality and impact of my work.