<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Md. Ajhar Uddin</title>
    <description>The latest articles on DEV Community by Md. Ajhar Uddin (@ajhar146).</description>
    <link>https://dev.to/ajhar146</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4176803%2F9d93f362-1d9d-4baa-b63e-3f2bea01353c.jpg</url>
      <title>DEV Community: Md. Ajhar Uddin</title>
      <link>https://dev.to/ajhar146</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ajhar146"/>
    <language>en</language>
    <item>
      <title>I built an anonymous teacher review site for my university. The code was the easy part.</title>
      <dc:creator>Md. Ajhar Uddin</dc:creator>
      <pubDate>Sun, 11 Oct 2026 10:29:40 +0000</pubDate>
      <link>https://dev.to/ajhar146/i-built-an-anonymous-teacher-review-site-for-my-university-the-code-was-the-easy-part-3p51</link>
      <guid>https://dev.to/ajhar146/i-built-an-anonymous-teacher-review-site-for-my-university-the-code-was-the-easy-part-3p51</guid>
      <description>&lt;p&gt;I'm a CSE student at Rajshahi University of Engineering &amp;amp; Technology (RUET).&lt;br&gt;
Every semester the same conversation happens in every batch: &lt;em&gt;what is that&lt;br&gt;
teacher actually like?&lt;/em&gt; The answer travels by word of mouth, unevenly, and&lt;br&gt;
juniors mostly get nothing.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://ruet-faculty-review.vercel.app" rel="noopener noreferrer"&gt;RUET Faculty Review&lt;/a&gt; —&lt;br&gt;
anonymous student reviews of teaching, closed to RUET email addresses.&lt;br&gt;
Next.js, Prisma, Postgres, Vercel. None of that was the hard part.&lt;/p&gt;

&lt;p&gt;Three things were.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. A moderation queue nobody reads is a graveyard
&lt;/h2&gt;

&lt;p&gt;The obvious design: review comes in, goes to a queue, admin approves it. I&lt;br&gt;
built it, then imagined it two weeks into exams and realised I would never&lt;br&gt;
open it. Students' writing would just sit there.&lt;/p&gt;

&lt;p&gt;So the AI decides everything — Groq, JSON Schema mode, four verdicts.&lt;br&gt;
&lt;code&gt;PENDING&lt;/code&gt; now means exactly one thing: &lt;strong&gt;the AI could not be reached&lt;/strong&gt;. A&lt;br&gt;
cron retries those, and I get one alert, ever, if moderation has been down&lt;br&gt;
for hours.&lt;/p&gt;

&lt;p&gt;My first prompt was also too strict. It rejected a review saying a teacher&lt;br&gt;
explains too fast and skips questions — exactly the honest criticism the site&lt;br&gt;
exists to collect. The rewrite leads with &lt;strong&gt;DEFAULT TO SAFE&lt;/strong&gt;: harsh, angry,&lt;br&gt;
informal criticism of &lt;em&gt;teaching&lt;/em&gt; is fine. Only personal attacks, accusations&lt;br&gt;
and spam are not. An over-strict filter empties the site.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. &lt;code&gt;AVG(rating)&lt;/code&gt; is not one student, one vote
&lt;/h2&gt;

&lt;p&gt;Five students review a teacher. One wrote eight reviews — they took eight of&lt;br&gt;
that teacher's courses — all 1 star. Four others wrote one each, all 3 stars.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flat average:   (1×8 + 3×4) / 12  =  1.67
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One person outvoted four, using entirely legitimate reviews.&lt;/p&gt;

&lt;p&gt;The fix is a mean of means — average each student first, then average those:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;one student, one vote:   (1 + 3 + 3 + 3 + 3) / 5  =  2.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per-course averages on a teacher's page still use the plain average,&lt;br&gt;
deliberately. There the question is "what is this course like", and each&lt;br&gt;
review is one answer to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A server action is an HTTP endpoint, whether you think of it as one or not
&lt;/h2&gt;

&lt;p&gt;I had a helper — &lt;code&gt;teacherCapProblem(userId, teacherId)&lt;/code&gt; — exported from a&lt;br&gt;
&lt;code&gt;'use server'&lt;/code&gt; file because a page needed it. It answers &lt;em&gt;"has user X&lt;br&gt;
reviewed teacher Y, and how often"&lt;/em&gt;. The page it served was behind a login.&lt;br&gt;
The helper was not.&lt;/p&gt;

&lt;p&gt;On a site whose entire promise is that nobody learns who wrote what, that is&lt;br&gt;
the one question it must never answer.&lt;/p&gt;

&lt;p&gt;The fix wasn't to add an auth check. It was to move it to a plain module, so&lt;br&gt;
the endpoint stops existing. &lt;strong&gt;A helper that takes a &lt;code&gt;userId&lt;/code&gt; should never be&lt;br&gt;
exported from a &lt;code&gt;'use server'&lt;/code&gt; file.&lt;/strong&gt; There's now a test that fails if any&lt;br&gt;
exported action lacks a session check.&lt;/p&gt;




&lt;p&gt;Three lessons, if you're building something similar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write the honest version of your constraint first.&lt;/strong&gt; "I'll check the
queue daily" was a lie I told myself, and a week of work sat on top of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Print the artefact, don't trust the generator.&lt;/strong&gt; My &lt;code&gt;robots.txt&lt;/code&gt;
generator emitted &lt;code&gt;Allow: /&lt;/code&gt;, which cancelled &lt;code&gt;Disallow: /&lt;/code&gt; and opened
every teacher page to crawling. Reading the generated file caught it;
reading the code had not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the failure path.&lt;/strong&gt; A health check that returns 200 while the
database is down is worse than none, because now you trust it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The site is closed to RUET students, which is the point — it isn't affiliated&lt;br&gt;
with the university. If you've built something similar, I'd like to hear how&lt;br&gt;
you handled moderation. That's the part I'm still least sure about.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruet-faculty-review.vercel.app" rel="noopener noreferrer"&gt;ruet-faculty-review.vercel.app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>showdev</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
