<?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: Pedro “PT” Thomaz</title>
    <description>The latest articles on DEV Community by Pedro “PT” Thomaz (@pedro_ptthomaz_7ba07ec).</description>
    <link>https://dev.to/pedro_ptthomaz_7ba07ec</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%2F4079926%2F5afc26ef-5b55-4637-9e37-56673af557ae.jpg</url>
      <title>DEV Community: Pedro “PT” Thomaz</title>
      <link>https://dev.to/pedro_ptthomaz_7ba07ec</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pedro_ptthomaz_7ba07ec"/>
    <language>en</language>
    <item>
      <title>I built a native Mac Markdown editor solo, with zero dependencies. It launched on Product Hunt today</title>
      <dc:creator>Pedro “PT” Thomaz</dc:creator>
      <pubDate>Wed, 11 Nov 2026 00:31:37 +0000</pubDate>
      <link>https://dev.to/pedro_ptthomaz_7ba07ec/i-built-a-native-mac-markdown-editor-solo-with-zero-dependencies-it-launched-on-product-hunt-today-131f</link>
      <guid>https://dev.to/pedro_ptthomaz_7ba07ec/i-built-a-native-mac-markdown-editor-solo-with-zero-dependencies-it-launched-on-product-hunt-today-131f</guid>
      <description>&lt;p&gt;Hi DEV! I'm Pedro, and for the past months I've been building &lt;strong&gt;Coffee&lt;/strong&gt;, a Markdown editor for the Mac, on my own. It went live on Product Hunt today, so here's what it is and a few things I learned building it.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/fQckx9XMnIg" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plain files.&lt;/strong&gt; Every note is a &lt;code&gt;.md&lt;/code&gt; file in a folder you choose. No database for your writing, no import step, and any other editor can open the same folder (an Obsidian vault works as it is).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answers beside the line.&lt;/strong&gt; Write &lt;code&gt;20% of 50&lt;/code&gt;, &lt;code&gt;$30 in EUR&lt;/code&gt;, &lt;code&gt;Tokyo time&lt;/code&gt; or &lt;code&gt;half of 175&lt;/code&gt; and the answer appears next to the line, without going into your file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maths and Mermaid, drawn natively.&lt;/strong&gt; No web view: TeX and Mermaid diagrams are rendered by the app itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review every AI edit.&lt;/strong&gt; When Claude, Cursor or anything else changes a note's file, Coffee lays out every change and you keep or undo each one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP, off by default.&lt;/strong&gt; AI apps can read and write your notes over MCP, but only once you turn it on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it's built
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Swift 6.2 and SwiftPM, macOS 15 and later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero third-party dependencies.&lt;/strong&gt; The Markdown parser, the Mermaid and TeX renderers and the calculator are all written for the app. Search runs on the system's SQLite.&lt;/li&gt;
&lt;li&gt;Separate small executables for the MCP server, Quick Look and the updater, so each one does one job.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing my own Mermaid renderer sounded like a bad idea at first, but it means diagrams appear instantly, look like the rest of the app and work offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned on launch day
&lt;/h2&gt;

&lt;p&gt;The launches above mine came in with three to five times more Product Hunt followers than I had. Followers before the day matter far more than anything you do on the day. If you're planning a launch, start collecting them weeks earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;14 days free, no card or account: &lt;a href="https://coffeetext.app" rel="noopener noreferrer"&gt;coffeetext.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's in its final hours on Product Hunt, and I'd love honest feedback there or here: &lt;a href="https://www.producthunt.com/products/coffee-9" rel="noopener noreferrer"&gt;Coffee on Product Hunt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What would make you switch from your current Markdown editor?&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>swift</category>
      <category>macos</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Why I shipped rules and statistics instead of an LLM for my health app</title>
      <dc:creator>Pedro “PT” Thomaz</dc:creator>
      <pubDate>Sun, 16 Aug 2026 09:57:44 +0000</pubDate>
      <link>https://dev.to/pedro_ptthomaz_7ba07ec/why-i-shipped-rules-and-statistics-instead-of-an-llm-for-my-health-app-33cj</link>
      <guid>https://dev.to/pedro_ptthomaz_7ba07ec/why-i-shipped-rules-and-statistics-instead-of-an-llm-for-my-health-app-33cj</guid>
      <description>&lt;p&gt;I build Vitra, a desktop app that reads your Oura Ring data and gives you one plain sentence each morning about how your body is doing. The obvious way to build that in 2026 is to pipe the numbers into an LLM. I went a different way, and I think the reasoning generalises past health apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with the LLM version
&lt;/h2&gt;

&lt;p&gt;Exporting a month of JSON into a chat window every morning is a chore, but that is a UX problem and UX problems are solvable.&lt;/p&gt;

&lt;p&gt;The harder problem is stability. Push back on a recommendation and the model tends to fold and tell you the opposite. Same numbers, same night of sleep, different advice, because you argued.&lt;/p&gt;

&lt;p&gt;That is fatal for this particular product. If the app says take a rest day, the entire value is that it holds that position when you do not want to hear it. An opinion you can talk the model out of in one message is a mirror with extra steps.&lt;/p&gt;

&lt;p&gt;This is sycophancy. It is well documented, and it is not something you fix with a system prompt telling the model to be firm. You can reduce it. You cannot make it deterministic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I shipped instead
&lt;/h2&gt;

&lt;p&gt;Transparent rules and statistics computed over the user's own history:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Establish a personal baseline per metric over the first one to two weeks, rather than comparing against a population average&lt;/li&gt;
&lt;li&gt;Detect deviations from that baseline, not from a global "normal"&lt;/li&gt;
&lt;li&gt;Require multi-day agreement before making a confident call, so one bad night does not trigger anything&lt;/li&gt;
&lt;li&gt;Encode the decision thresholds explicitly, before the app has seen today's data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is the one that matters. The rule is committed to in advance, today's numbers get evaluated against it, and the user arguing does not change the arithmetic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What that buys you
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Determinism.&lt;/strong&gt; Same inputs, same output, every time. You can write tests. With a model in that position, "did it give the right answer" is a vibes check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explainability.&lt;/strong&gt; When it says recovery is trending down it can point at which signals moved and by how much. That is the actual computation, not a post-hoc rationalisation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regulatory headroom.&lt;/strong&gt; Under EU MDR Rule 11, software intended to inform a diagnostic or therapeutic decision starts at Class IIa. A wellness app has to stay clearly on one side of that line, and it is much easier to argue about a rule you can print out than about a model's output distribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It runs locally.&lt;/strong&gt; No inference API calls means no account and no server doing the analysis, and health data that never leaves the machine. To be precise, since people ask: the licence check does talk to a server when you activate a key, and that is the only call that leaves. It carries no health data. This was the deciding factor. Local-first with a frontier model in the loop was not going to happen, and the privacy story turned out to be what people actually cared about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I underrated: you can correct it
&lt;/h2&gt;

&lt;p&gt;A fixed rule is a thing you can teach. If Vitra blames your late workout for a bad night and that is wrong for you, you say so, and the correction sticks. Your thresholds drift toward you over time instead of sitting at the average of everyone.&lt;/p&gt;

&lt;p&gt;Determinism is what makes that possible. Something that rerolls its own reasoning every morning has nothing stable to correct, so there is nothing to teach.&lt;/p&gt;

&lt;p&gt;Worth being precise about what determinism means here. Same inputs give the same output, and your corrections are part of the inputs. The sentence can change over time, and when it does you can point at exactly which correction moved it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a language model still earns its place
&lt;/h2&gt;

&lt;p&gt;I am not arguing against LLMs. I am arguing against putting one on the critical path of a decision that has to be stable.&lt;/p&gt;

&lt;p&gt;Phrasing is a good use. Turning a computed result into a sentence that sounds human is exactly what these models are good at, and if that layer is sycophantic the worst case is slightly awkward wording. Freeform Q&amp;amp;A over your own history is another. Both sit downstream of the judgement rather than being the judgement.&lt;/p&gt;

&lt;p&gt;The heuristic I ended up with: if you would be upset by the system changing its answer because the user pushed back, do not put a language model in that position.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;Rules do not generalise for free. Every new signal is real work, and a threshold that looks sensible on paper can turn out to be noise once it meets real data. A model would have "handled" those cases immediately, just not necessarily correctly, and you would not know which ones.&lt;/p&gt;

&lt;p&gt;There is also a real product cost. The app stays deliberately quiet for the first couple of weeks while it learns your baseline, which is a strange thing to ship when every competitor tries to impress you on day one. I still think it is the right call.&lt;/p&gt;

&lt;p&gt;Here is what it actually looks like in the morning:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/LvPAw6Tmn0k" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Vitra is at &lt;a href="https://vitrahealth.app" rel="noopener noreferrer"&gt;https://vitrahealth.app&lt;/a&gt; if you want to see what it turned into. Mac and Windows, one-time purchase, no subscription.&lt;/p&gt;

&lt;p&gt;Happy to be argued with, especially by anyone who has made the sycophancy problem tractable in production.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>privacy</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
