<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Developing with Michael]]></title><description><![CDATA[Software Engineering]]></description><link>https://blog.aboff.com</link><image><url>https://substackcdn.com/image/fetch/$s_!zyWX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe052a460-fff4-40f4-8110-9152d9e99847_256x256.png</url><title>Developing with Michael</title><link>https://blog.aboff.com</link></image><generator>Substack</generator><lastBuildDate>Thu, 30 Apr 2026 07:27:06 GMT</lastBuildDate><atom:link href="https://blog.aboff.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Michael Aboff]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[aboff@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[aboff@substack.com]]></itunes:email><itunes:name><![CDATA[Michael Aboff]]></itunes:name></itunes:owner><itunes:author><![CDATA[Michael Aboff]]></itunes:author><googleplay:owner><![CDATA[aboff@substack.com]]></googleplay:owner><googleplay:email><![CDATA[aboff@substack.com]]></googleplay:email><googleplay:author><![CDATA[Michael Aboff]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Installing OpenBUGS 3.2.3 on Windows, Linux/Ubuntu, and Mac]]></title><description><![CDATA[Whenever taking a higher education, I&#8217;m shocked by how poorly documentation for critical software is maintained.]]></description><link>https://blog.aboff.com/p/installing-openbugs-323-on-windows</link><guid isPermaLink="false">https://blog.aboff.com/p/installing-openbugs-323-on-windows</guid><dc:creator><![CDATA[Michael Aboff]]></dc:creator><pubDate>Wed, 28 Aug 2024 02:16:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zyWX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe052a460-fff4-40f4-8110-9152d9e99847_256x256.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Whenever taking a higher education, I&#8217;m shocked by how poorly documentation for critical software is maintained. Hopefully this article will help anyone else trying to get OpenBUGS working on their Linux machine. Unfortunately OpenBUGS is a discontinued software, but sure as heck my Bayesian Statistics course is still using it.</p><h1>Windows</h1><p>This worked for me on Windows 11, but your mileage may vary (especially since it hasn&#8217;t been under development for a while).</p><ol><li><p>Download the <a href="https://drive.google.com/file/d/1zHPEC3b77_GQmRAiW_f8SWo4SNmou66a/view?usp=drive_link">installer executable from here</a>. (If you don&#8217;t trust me, you can find the same <a href="https://www.mrc-bsu.cam.ac.uk/software">installer from here</a>).</p></li><li><p>Extract the file.</p></li><li><p>Run the installer.</p></li></ol><h1>Linux</h1><p>I&#8217;ve only started using this program, so I&#8217;m not sure if everything is supported on Linux. Use at your own risk!</p><h2>WINE</h2><p>Unfortunately linux does not enjoy a native GUI experience for OpenBUGS. The best I&#8217;ve found to progress is using WINE with the Windows version.</p><h3>Installation Instructions</h3><ol><li><p><a href="https://drive.google.com/file/d/1PtXWIkEENxuHM8LgSxpIymCxQ8sq7eV9/view?usp=sharing">Download OpenBUGS323.zip</a> . (If you don&#8217;t trust me, you can find the same <a href="https://www.mrc-bsu.cam.ac.uk/software">compressed OpenBUGS323.zip from here</a>).</p></li><li><p>Extract the folder to OpenBUGS323/</p></li><li><p>Follow <a href="https://wiki.winehq.org/Download">the instructions for your distribution</a> to download and install WINE.</p></li><li><p>CD into your OpenBUGS323 folder</p></li><li><p>Run <code>wine OpenBUGS.exe</code></p></li></ol><p><em>Note: If your WINE app is rendering really, really small you can run </em><code>winecfg</code><em> in the terminal which will open a configuration window. Open the Graphics tab and alter the &#8220;Screen resolution&#8221; slider until you get to a size that works for you.</em></p><p><em>If you are using Ubuntu, you can create a custom launcher for it by <a href="https://linuxconfig.org/how-to-create-desktop-shortcut-launcher-on-ubuntu-22-04-jammy-jellyfish-linux">following these instructions</a>. You can make the openbugs.desktop file similar to how I made mine. Make sure to set your Exec command to match where your OpenBUGS.exe file is located :</em></p><pre><code>[Desktop Entry]
Name=OpenBUGS
Comment=Launch OpenBUGS
Keywords=openbugs;stats;statistics;
OnlyShowIn=GNOME;Unity;
Exec=wine /home/michael/Documents/Applications/OpenBUGS323/OpenBUGS.exe
StartupNotify=true
Terminal=false
Type=Application
Categories=Utility;
MimeType=x-scheme-handler/ghelp;x-scheme-handler/help;x-scheme-handler/info;x-scheme-handler/man;</code></pre><h2>Native Linux (CLI only)</h2><p>I did some investigation into the official command line interface for future experimentation and wanted to share it with you below. I also believe this only works on Intel processors.</p><h3>Installation Instructions</h3><ol><li><p>Download <a href="https://drive.google.com/file/d/1Qmxob0F83N4MnM2WQz4MbegsYK6YdVKA/view?usp=sharing">the tar.gz OpenBugs file</a>.</p></li><li><p>Extract the file either via your OS&#8217;s built in toolset, or (if you&#8217;re feeling spicy) run the following commands in the terminal:</p><ol><li><p><code>gzip -d OpenBUGS-3.2.3.tar.gz</code></p></li><li><p><code>tar -xvzf OpenBUGS-3.2.3.tar</code></p></li></ol></li><li><p>CD into the directory</p><ol><li><p><code>cd OpenBUGS-3.2.3</code></p></li></ol></li><li><p>Run <code>./configure</code></p><ol><li><p>If you get an error saying &#8220;In file included from OpenBUGSCli.c:2:</p><p>/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: No such file or directory&#8221; you will need to install gcc-multilib</p><ol><li><p><code>sudo apt-get install gcc-multilib</code></p></li></ol></li></ol></li><li><p>Run <code>make</code></p></li><li><p>Run <code>sudo make install</code></p></li></ol><p>For more detailed installation instructions, review the README or INSTALL files located in the OpenBUGS-3.2.3 file.</p><h3>Usage Instructions</h3><p>The command <code>OpenBUGS</code> should be accessible anywhere in your terminal. I would recommend reading through the README and <code>man OpenBUGS</code> commands to get a gist of how it works. You can open an interactive interpreter by running <code>OpenBUGS</code> without any arguments.</p><h1>Mac</h1><p>I haven&#8217;t tested this, however the installation instructions are very similar to the linux + WINE ones that I listed above. Here is what I&#8217;ve found may work for you (<a href="https://github.com/sooahnshin/BUGS-on-Mac">courtesy of sooahnshin on github</a> and <a href="https://sites.google.com/site/mmeclimate/-bayesmet/openbugs-on-mac-os-x">Zero Grau</a>). Note that this may also only work on Intel processors (maybe ones with Apple silicon + the Rosetta layer?).</p><ol><li><p>Download the <a href="https://drive.google.com/file/d/1zHPEC3b77_GQmRAiW_f8SWo4SNmou66a/view?usp=drive_link">installer executable from here</a>. (If you don&#8217;t trust me, you can find the same <a href="https://www.mrc-bsu.cam.ac.uk/software">installer from here</a>.</p></li><li><p>Extract the .exe installer from the zip file.</p></li><li><p>Download and install <a href="https://brew.sh/">Homebrew</a>.</p></li><li><p>Open the terminal and run <code>brew install wine</code></p></li><li><p>In your terminal, to the Downloads folder where you downloaded and extracted the installer exe file.</p></li><li><p>Run <code>wine OpenBUGS323setup.exe</code></p></li><li><p>Theoretically this should have installed the app. Good luck!</p></li></ol>]]></content:encoded></item><item><title><![CDATA[BoffMvc - Case Study]]></title><description><![CDATA[A custom PHP MVC framework - PHP, MySQL, Javascript, CSS3, HTML5]]></description><link>https://blog.aboff.com/p/boffmvc</link><guid isPermaLink="false">https://blog.aboff.com/p/boffmvc</guid><dc:creator><![CDATA[Michael Aboff]]></dc:creator><pubDate>Thu, 06 Jun 2024 06:53:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!q0uF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q0uF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q0uF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png 424w, https://substackcdn.com/image/fetch/$s_!q0uF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png 848w, https://substackcdn.com/image/fetch/$s_!q0uF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!q0uF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!q0uF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png" width="1456" height="817" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:817,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2112773,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!q0uF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png 424w, https://substackcdn.com/image/fetch/$s_!q0uF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png 848w, https://substackcdn.com/image/fetch/$s_!q0uF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!q0uF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa307649e-2a9c-4ab9-adaa-edec4591b98e_1782x1000.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://github.com/mwaboff/boffmvc&quot;,&quot;text&quot;:&quot;Code&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://github.com/mwaboff/boffmvc"><span>Code</span></a></p><h3>How building my own router, database layer, and template system taught me what Laravel and Django abstract away</h3><p>I&#8217;ve shipped apps with Django and Ruby on Rails, but I always had a nagging feeling that I didn&#8217;t truly understand what was happening under the hood. When something broke in the routing layer or a query didn&#8217;t behave the way I expected, I was guessing. I was using the magic without understanding the trick.</p><p>So I decided to build my own MVC framework from scratch in PHP. No third party libraries, no ORM, no training wheels. Just me, PHP, MySQL, and the question: <em>what does it actually take to make one of these things work?</em></p><p>The result is <a href="https://github.com/mwaboff/boffmvc">BoffMVC</a>, and the process of building it taught me more about web architecture than any tutorial or course ever could.</p><h3>Convention Over Configuration: A Router That Figures It Out</h3><p>One of the first big decisions I had to make was how routing would work. If you&#8217;ve used Laravel, you know the pattern: you explicitly define every single route in a file.</p><p>php</p><pre><code><code>// Laravel's approach - manually define each route
Route::get('/user', [UserController::class, 'index']);
Route::get('/recipe', [RecipeController::class, 'index']);
Route::post('/recipe', [RecipeController::class, 'store']);</code></code></pre><p>This works great in production frameworks, but I wanted to see if I could take a different approach: what if the router could figure out which controller to use just by looking at the URL? Instead of maintaining a growing list of route definitions, BoffMVC dynamically resolves the controller from the request.</p><p>php</p><pre><code><code>// Router.php - dynamically resolve the controller from the URL
$page = isset($_GET['page']) ? $_GET['page'] : 'home';
$controller_name = ucfirst($page) . 'Controller';
$controller_path = 'app/controller/' . $controller_name . '.php';

if (file_exists($controller_path)) {
    require_once($controller_path);
    $controller = new $controller_name();
} else {
    // Fall back to the home page if no matching controller exists
    require_once('app/controller/HomeController.php');
    $controller = new HomeController();
}</code></code></pre><p>When a user visits <code>example.com/?page=user</code>, the router parses out <code>user</code>, looks for a <code>UserController</code>, and loads it. If that controller doesn&#8217;t exist, it gracefully falls back to the <code>HomeController</code>. No route file to maintain, no manual wiring.</p><p>The tradeoff here is flexibility. With Laravel&#8217;s explicit routing, you can map any URL pattern to any controller method. My convention-based approach means you&#8217;re locked into a specific naming pattern. But for this project, the simplicity was worth it and it forced a consistent structure across the whole application.</p><p>Before any of that controller resolution happens though, the router first checks the CSRF token to protect against cross-site request forgery. Every form rendered by BoffMVC includes a hidden token field, and the router validates it before processing any request:</p><p>php</p><pre><code><code>// Validate CSRF token before processing the request
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        die('Invalid CSRF token');
    }
}</code></code></pre><p>This was one of those features that I never thought twice about in Django (it just works) but building it myself gave me a real appreciation for how important it is to bake security into the framework layer rather than leaving it to individual developers.</p><h3>Simulating RESTful Methods Over POST</h3><p>Here&#8217;s a practical constraint I ran into early: HTML forms only support GET and POST. There&#8217;s no native way to send a PUT or DELETE request from a form. I&#8217;d never had to think about this before because Rails and Django handle it behind the scenes. But when you&#8217;re building from scratch, you hit this wall fast.</p><p>My solution was to embed a hidden field in every form that tells the controller what the <em>intended</em> method is:</p><p>html</p><pre><code><code>&lt;!-- A form that should trigger a DELETE action --&gt;
&lt;form method="POST" action="index.php?page=recipe"&gt;
    &lt;input type="hidden" name="csrf_token" value="&lt;?php echo $_SESSION['csrf_token']; ?&gt;"&gt;
    &lt;input type="hidden" name="_method" value="DELETE"&gt;
    &lt;input type="hidden" name="recipe_id" value="42"&gt;
    &lt;button type="submit"&gt;Delete Recipe&lt;/button&gt;
&lt;/form&gt;</code></code></pre><p>Then in the controller, I check for that hidden field and route accordingly:</p><p>php</p><pre><code><code>// ApplicationController.php - base controller that all others extend
class ApplicationController {
    public function manageRequest() {
        if ($_SERVER['REQUEST_METHOD'] === 'GET') {
            $this-&gt;handleGet();
        } else if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $method = isset($_POST['_method']) ? $_POST['_method'] : 'CREATE';
            switch ($method) {
                case 'PUT':
                    $this-&gt;handlePut();
                    break;
                case 'DELETE':
                    $this-&gt;handleDelete();
                    break;
                default:
                    $this-&gt;handleCreate();
                    break;
            }
        }
    }
}</code></code></pre><p>Every controller in BoffMVC extends <code>ApplicationController</code> and calls <code>manageRequest()</code> after instantiation. This pattern means each controller just needs to implement the specific handler methods it cares about (<code>handleGet</code>, <code>handleCreate</code>, <code>handlePut</code>, <code>handleDelete</code>) and the base class takes care of dispatching.</p><p>I later learned that Rails does almost the exact same thing with its <code>_method</code> hidden field. It was a fun moment of convergent design, arriving at the same solution independently because the underlying constraint is the same for everyone.</p><h3>When Textbook MVC Breaks Down: Discovering the Model Manager Pattern</h3><p>This was probably the biggest &#8220;aha&#8221; moment of the whole project. The textbook MVC diagram is clean: Controller talks to Model, Model talks to Database, Model returns data to View. Simple.</p><p>In practice, it got messy fast. My <code>User</code> model represented a single user and handled things like saving to the database or updating a password. But where does the logic go for &#8220;find all users who signed up this week&#8221; or &#8220;check if this email already exists before creating a new account&#8221;? Those operations aren&#8217;t really about a single user object. They&#8217;re about managing <em>collections</em> of users.</p><p>I ended up splitting model responsibilities into two classes: the Model and the Model Manager.</p><p>php</p><pre><code><code>// User.php - represents a single user object
class User {
    public $id;
    public $username;
    public $email;

    public function __construct($data) {
        $this-&gt;id = $data['id'];
        $this-&gt;username = $data['username'];
        $this-&gt;email = $data['email'];
    }

    public function save() {
        $db = new DBManager();
        $db-&gt;query(
            "UPDATE users SET username = ?, email = ? WHERE id = ?",
            [$this-&gt;username, $this-&gt;email, $this-&gt;id]
        );
    }
}</code></code></pre><p>php</p><pre><code><code>// UserManager.php - manages collections and queries about users
class UserManager {
    public function getUserById($id) {
        $db = new DBManager();
        $result = $db-&gt;query("SELECT * FROM users WHERE id = ?", [$id]);
        return $result ? new User($result) : null;
    }

    public function getAllUsers() {
        $db = new DBManager();
        $results = $db-&gt;query("SELECT * FROM users");
        return array_map(function($row) { return new User($row); }, $results);
    }

    public function emailExists($email) {
        $db = new DBManager();
        $result = $db-&gt;query("SELECT id FROM users WHERE email = ?", [$email]);
        return !empty($result);
    }
}</code></code></pre><p>The Model handles individual object behavior (save, update, delete itself). The Model Manager handles finding, filtering, and creating those objects. I later learned that this separation is similar to what&#8217;s called the <a href="https://en.wikipedia.org/wiki/Repository_pattern">Repository pattern</a> in domain-driven design, and it&#8217;s essentially what Django&#8217;s Manager class does (e.g., <code>User.objects.filter(...)</code>). I arrived at it independently because the single-model approach just couldn&#8217;t handle queries that spanned multiple objects cleanly.</p><p>This was the moment where building my own framework paid off the most. I went from vaguely knowing that Django has a <code>.objects</code> manager to understanding <em>why</em> it exists and what problem it solves.</p><h3>The Database Layer: Simplicity Over Abstraction</h3><p>Established frameworks like Laravel come with full Object-Relational Mapping (ORM) systems like Eloquent, which let you interact with database tables as if they were PHP objects. That&#8217;s powerful, but it&#8217;s also a huge amount of abstraction to build.</p><p>I went the opposite direction. BoffMVC provides a thin <code>DBManager</code> interface that handles connections and parameterized queries, but you write the SQL yourself:</p><p>php</p><pre><code><code>// DBManager.php - thin wrapper around PDO
class DBManager {
    private $connection;

    public function __construct() {
        $this-&gt;connection = new PDO(
            'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME,
            DB_USER,
            DB_PASS
        );
        $this-&gt;connection-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }

    public function query($sql, $params = []) {
        $stmt = $this-&gt;connection-&gt;prepare($sql);
        $stmt-&gt;execute($params);
        return $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);
    }
}</code></code></pre><p>Every time a model or manager needs to talk to the database, it creates a new <code>DBManager</code> instance, which opens a connection, runs the query, and returns the result. The pattern ends up looking like this throughout the codebase:</p><pre><code><code>$db = new DBManager();
$recipes = $db-&gt;query(
    "SELECT * FROM recipes WHERE user_id = ? ORDER BY created_at DESC",
    [$user_id]
);</code></code></pre><p>Is this the most efficient approach? No. Creating a new connection per query is wasteful compared to connection pooling. A production framework would maintain a single connection (or a pool) across the request lifecycle. But for a learning project, the simplicity of &#8220;new instance, run query, get result&#8221; meant I could focus on understanding the <em>flow</em> of data through the application without getting bogged down in connection management.</p><p>If I were to rebuild this today, I&#8217;d implement a singleton pattern for the database connection so that all queries within a single request share one connection.</p><h3>Views and the Template System</h3><p>The view layer in BoffMVC collects data from the controller and passes it into <code>.phtml</code> template files. All data that needs to be rendered gets packed into a <code>$RENDER_VARS</code> array:</p><pre><code><code>// RecipeView.php
class RecipeView {
    public function showRecipe($recipe, $author) {
        $RENDER_VARS = [];
        $RENDER_VARS['title'] = $recipe-&gt;title;
        $RENDER_VARS['author'] = $author-&gt;username;
        $RENDER_VARS['ingredients'] = $recipe-&gt;ingredients;

        // Load CSS and JS specific to this page
        $RENDER_VARS['css'] = ['app/view/Recipe/css/recipe.css'];
        $RENDER_VARS['js'] = ['app/view/Recipe/js/recipe.js'];

        // Hand off to the template
        require('app/template/recipe_show.phtml');
    }
}</code></code></pre><p>Templates were broken up into reusable sub-templates: a header, a footer, a navigation bar, and then page-specific content in between. This meant I could change the site-wide navigation in one place instead of updating every page. It&#8217;s the same concept as Django&#8217;s template inheritance or Rails&#8217; layouts, just implemented more manually.</p><h3>The Flow of a Request, End to End</h3><p>To tie it all together, here&#8217;s what happens when a user visits <code>example.com/?page=recipe&amp;id=5</code>:</p><ol><li><p>The browser hits <code>index.php</code>, which immediately hands off to <code>Router.php</code>.</p></li><li><p>The router checks the CSRF token (for POST requests), parses <code>page=recipe</code> from the URL, and loads <code>RecipeController.php</code>.</p></li><li><p>The controller calls <code>manageRequest()</code>, which identifies this as a GET request and calls <code>handleGet()</code>.</p></li><li><p><code>handleGet()</code> asks <code>RecipeManager</code> to find the recipe with <code>id=5</code>. The manager creates a <code>DBManager</code>, runs the query, and returns a <code>Recipe</code> object.</p></li><li><p>The controller passes the <code>Recipe</code> object to <code>RecipeView</code>.</p></li><li><p>The view packs the data into <code>$RENDER_VARS</code> and loads the appropriate <code>.phtml</code> template.</p></li><li><p>The template renders the final HTML back to the user.</p></li></ol><p>Each layer has a single responsibility, and data flows in one direction. That&#8217;s the whole point of MVC, and building it from the ground up made each of those boundaries tangible instead of theoretical.</p><h3>What I&#8217;d Do Differently</h3><p>The biggest mistake I made was developing the framework while simultaneously building a recipe sharing website on top of it. The framework and the application code became intertwined. Features got added to the framework because the recipe site needed them, not because they were generalized solutions. This made BoffMVC difficult to reuse for any other project.</p><p>If I were starting over, I&#8217;d develop the framework as a standalone library with its own test suite, and build the recipe site as a completely separate project that imports it. That separation of concerns applies at the project level, not just the code level.</p><p>I&#8217;d also invest in a few specific improvements. First, a singleton or factory pattern for database connections instead of creating a new connection per query. Second, a more flexible routing system that supports clean URLs (like <code>/recipe/5</code> instead of <code>?page=recipe&amp;id=5</code>). Third, better error handling and logging rather than the bare-bones approach I took.</p><h3>The Point of All This</h3><p>Building BoffMVC wasn&#8217;t about creating something to compete with Laravel. It was about pulling back the curtain on tools I use every day and understanding the decisions that went into them. Every professional framework has made hundreds of architectural choices. Until you&#8217;ve faced even a handful of those choices yourself, it&#8217;s hard to appreciate why they made the tradeoffs they did.</p><p>I now have a much deeper understanding of routing, request lifecycles, database abstraction layers, and the MVC pattern itself. When I work with Laravel or Django now, I&#8217;m not just following the documentation. I understand the <em>why</em> behind the patterns, and that makes me a better engineer.</p>]]></content:encoded></item><item><title><![CDATA[HF Enhancement Suite - Case Study]]></title><description><![CDATA[A cross-browser extension that built an enthusiastic community of over 5000 users.]]></description><link>https://blog.aboff.com/p/hf-enhancement-suite-case-study</link><guid isPermaLink="false">https://blog.aboff.com/p/hf-enhancement-suite-case-study</guid><dc:creator><![CDATA[Michael Aboff]]></dc:creator><pubDate>Thu, 06 Jun 2024 06:43:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!mtDh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mtDh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mtDh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png 424w, https://substackcdn.com/image/fetch/$s_!mtDh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png 848w, https://substackcdn.com/image/fetch/$s_!mtDh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!mtDh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mtDh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png" width="1456" height="912" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:912,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3004750,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mtDh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png 424w, https://substackcdn.com/image/fetch/$s_!mtDh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png 848w, https://substackcdn.com/image/fetch/$s_!mtDh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!mtDh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F324f6b49-d3b3-4c5d-b149-26e5673cfcce_1597x1000.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://github.com/mwaboff/boff-bakery-recipes&quot;,&quot;text&quot;:&quot;View Code&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://github.com/mwaboff/boff-bakery-recipes"><span>View Code</span></a></p><p>HF Enhancement Suite (HFES for short) is a userscript designed for the popular HackForums.net web forum. Userscripts are cross-browser add-ons that can add additional functionality to a website. HFES adds a variety of functionality including the ability to subscribe to forums, quote multiple posts at once, and a complete interface overhaul of the private messaging system. Over 5000 people have installed this popular application.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xm0Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xm0Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png 424w, https://substackcdn.com/image/fetch/$s_!xm0Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png 848w, https://substackcdn.com/image/fetch/$s_!xm0Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png 1272w, https://substackcdn.com/image/fetch/$s_!xm0Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xm0Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png" width="426" height="243.672" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1000,&quot;resizeWidth&quot;:426,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xm0Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png 424w, https://substackcdn.com/image/fetch/$s_!xm0Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png 848w, https://substackcdn.com/image/fetch/$s_!xm0Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png 1272w, https://substackcdn.com/image/fetch/$s_!xm0Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77472570-d58a-4e1d-9256-6b3c32d41645_1000x572.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Building a User Base</h3><p>At the time of I began developing the script, I was already building a good reputation across the site as a trustworthy and knowledgeable user. Like any internet forum, the website was filled with many cliques and I quickly started networking with other "high quality" posters. This networking was important, because when I started this passion project, these influential people were able to quickly become strong champions of the brand I built for myself.</p><p>HFES began as a passion script, trying to solve small problems and frustrations I was having with the website itself. It was not until I started discussing it with my colleagues that I realized the problems I were having was shared by other people. I realized, quickly, that tool could become something very special.</p><p>I began a small trial with some popular and well respected influencers on the website. As they were heavy users of the site, they would be a prime test subjects and could offer their own advice with their intimate knowledge of the platform. I collected a lot of feedback and held several brainstorming sessions on how to solve these problems with these select individuals. This ended up not only getting great ideas, but also the committed buy-in from these important users.</p><p>As soon as I released the script publicly, I encouraged these new brand ambassadors to post their reviews and help other users with any questions. This lent credibility to the project, something very important with a userbase as paranoid as on a site like Hack Forums.</p><p>At this time, I decided to focus on growth of the platform. Sourcing new features from the userbase helped early adopters feel apart of the process, exponentially increasing the userbase. The script spread almost entirely by word of mouth, with people voluntarily advertising in their post signatures (a valuable marketing location placed on every post they make).</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-9A0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-9A0!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif 424w, https://substackcdn.com/image/fetch/$s_!-9A0!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif 848w, https://substackcdn.com/image/fetch/$s_!-9A0!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif 1272w, https://substackcdn.com/image/fetch/$s_!-9A0!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-9A0!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif" width="650" height="165" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:165,&quot;width&quot;:650,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-9A0!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif 424w, https://substackcdn.com/image/fetch/$s_!-9A0!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif 848w, https://substackcdn.com/image/fetch/$s_!-9A0!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif 1272w, https://substackcdn.com/image/fetch/$s_!-9A0!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7150bba5-f706-404c-ba48-0180149fc7a5_650x165.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Monetization</h3><p>When my userbase started getting into the hundreds, I began adding more ambitious features that needed back-end support. I realized I needed to monetize it to keep it self-sustaining. Understanding the forum and the nature of a userscript, there was no way hide access of the script's features behind a paywall. Not only is it not technically feasible, but I would lose the large userbase entirely and a competitor would pop up immediately.</p><p>HFES had to stay free and open, so I decided to choose a freemium model. I could not add any pay-only core features, but I could add non-intrusive cosmetic perks that were visible to all users using the script.</p><p>I ended up selecting some small features that added a little icon next to a user's name, an animated username, or a gold bar present at the top of every post. Each feature was progressively more expensive in order to limit overall supply. This ended up making each feature considerably more popular than I originally thought, with people regularly purchasing the most expensive feature or all three at the same time.</p><h3>Takeaway</h3><p>In the end, the userbase extended over 5000 individuals and earned me over $4000. However, the money was not the most valuable takeaway from this project. Instead, despite this being one of my first projects, I learned some great lessons:</p><ol><li><p>Your personal brand and reputation is a valuable commodity.</p></li><li><p>Engaging respected influencers can quickly build up your own product's reputation.</p></li><li><p>If you respect your users, they will respect and support you.</p></li></ol><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://github.com/mwaboff/boff-bakery-recipes&quot;,&quot;text&quot;:&quot;View Code&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://github.com/mwaboff/boff-bakery-recipes"><span>View Code</span></a></p><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!da5u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!da5u!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png 424w, https://substackcdn.com/image/fetch/$s_!da5u!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png 848w, https://substackcdn.com/image/fetch/$s_!da5u!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png 1272w, https://substackcdn.com/image/fetch/$s_!da5u!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!da5u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png" width="400" height="516.9385194479297" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1030,&quot;width&quot;:797,&quot;resizeWidth&quot;:400,&quot;bytes&quot;:335038,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!da5u!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png 424w, https://substackcdn.com/image/fetch/$s_!da5u!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png 848w, https://substackcdn.com/image/fetch/$s_!da5u!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png 1272w, https://substackcdn.com/image/fetch/$s_!da5u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02579d51-ff59-4b18-a599-77acf05713ba_797x1030.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p>]]></content:encoded></item><item><title><![CDATA[MonkeyScripts.org - Case Study]]></title><description><![CDATA[A modern userscript hosting site built with PHP, Laravel, Javascript, React.js, MySQL, HTML5, CSS3, and Bootstrap]]></description><link>https://blog.aboff.com/p/monkeyscriptsorg-case-study</link><guid isPermaLink="false">https://blog.aboff.com/p/monkeyscriptsorg-case-study</guid><dc:creator><![CDATA[Michael Aboff]]></dc:creator><pubDate>Thu, 06 Jun 2024 06:32:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!uGoA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uGoA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uGoA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png 424w, https://substackcdn.com/image/fetch/$s_!uGoA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png 848w, https://substackcdn.com/image/fetch/$s_!uGoA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!uGoA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uGoA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png" width="1456" height="741" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:741,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2649481,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uGoA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png 424w, https://substackcdn.com/image/fetch/$s_!uGoA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png 848w, https://substackcdn.com/image/fetch/$s_!uGoA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!uGoA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F469412ca-f831-4df4-bfc8-6b1d1aff2652_2000x1018.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://monkeyscripts.net&quot;,&quot;text&quot;:&quot;Live Site&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://monkeyscripts.net"><span>Live Site</span></a></p><p></p><p>I have used and made user scripts for almost a decade and they have been a big part of <a href="https://aboff.com/projects/hfes.html">my growth as a developer</a>. When deciding on my Master's thesis, I was excited to research and build something that could help the community.</p><h3>What are User Scripts?</h3><p>User scripts are small cross-browser add-ons that can be used to enhance a user&#8217;s web browsing experience. These scripts are used to provide quick and unique customizations that directly modify the website a user is visiting. Created by the community, user scripts can add features ranging from adding detailed summary information from a review website into a favorite video streaming service to replacing all images with pictures of cats.</p><h3>The Problem</h3><p>The growth of the user script community has stagnated over the past 10 years as official browser extensions have gained popularity instead.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Zt78!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Zt78!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png 424w, https://substackcdn.com/image/fetch/$s_!Zt78!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png 848w, https://substackcdn.com/image/fetch/$s_!Zt78!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png 1272w, https://substackcdn.com/image/fetch/$s_!Zt78!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Zt78!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png" width="1445" height="766" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:766,&quot;width&quot;:1445,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Zt78!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png 424w, https://substackcdn.com/image/fetch/$s_!Zt78!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png 848w, https://substackcdn.com/image/fetch/$s_!Zt78!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png 1272w, https://substackcdn.com/image/fetch/$s_!Zt78!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053c5f94-63b9-40e0-b8cb-141777168a5d_1445x766.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>After interviewing technical and non-technical people of whom some were aware of what user scripts are, I realized there are two core problems:</p><ol><li><p><strong>User Acquisition:</strong> There are no easy to follow and understand explanations of what user scripts are and how to use them in a way that resonates with non technical users. To solve this I built an interactive tutorial that uses modern adult learning techniques, not something ever done in the user script community before.</p></li><li><p><strong>User Retention:</strong> To convince a user to try to learn, you have to provide obvious motivation as to why they would want to stay. To solve this, I built a collaborative filter recommendation system to provide relevant, personalized results.</p></li></ol><h3>User Acquisition - Interactive Tutorial</h3><p>Current research into effective adult learning techniques encourage teachers to follow these four core principles:</p><ul><li><p>Keep the lesson concise.</p></li><li><p>Let users practice what they are taught.</p></li><li><p>Use rewards as motivation.</p></li><li><p>Accommodate the three different learning styles: visual, auditory, and kinesthetic.</p></li></ul><p>To teach users, I created a tutorial that accommodated each of the above. To keep things concise, I broke the system down into two steps: 1) Install a user script manager; 2) find and install a script. To accommodate different visual and auditory learning styles, I included a video explaining how to install a user script manager. Where my tutorial differs than others, is that I created a script that I instructed users to install. When this kinesthetic task is completed, users will be immediately rewarded in the tutorial itself by updating the site itself. Constant feedback from early testers found this easier to understand than traditional wall-of-text tutorials other sites provide. You can check out the <a href="https://monkeyscripts.org/tutorial">live tutorial here</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!inKA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!inKA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png 424w, https://substackcdn.com/image/fetch/$s_!inKA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png 848w, https://substackcdn.com/image/fetch/$s_!inKA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png 1272w, https://substackcdn.com/image/fetch/$s_!inKA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!inKA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png" width="747" height="650" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:650,&quot;width&quot;:747,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!inKA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png 424w, https://substackcdn.com/image/fetch/$s_!inKA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png 848w, https://substackcdn.com/image/fetch/$s_!inKA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png 1272w, https://substackcdn.com/image/fetch/$s_!inKA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f213713-6a76-4f8a-9dcc-4b6ddd95902b_747x650.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>User Retention - Content Discovery</h3><p>New and existing users need to be provided a reason to continue learning and using any product. This is especially true with a niche and technical tool like user scripts. The best way to provide this motivation is by clearly showing users scripts that are relevant to them. Other user script hosting sites simply provide a list that can be sorted by top downloads or top rated, but often these are filled with complicated or irrelevant scripts. I believe that implementing a suggestion system, just like successful companies like Netflix and Amazon, would help users find things that are most relevant to them and convince them to stay.</p><p>I decided to use a similar type of recommendation system to the large companies: an item-based collaborative filtering recommendation system. This system records every user's interaction with scripts. If a user downloads a script, the interaction is rated higher than a simple visit to the description. The system will then compare every script with each other and, based off of the users that interacted with both scripts, calculate a similarity score.</p><p>To calculate this score, I decided to use the Pearson Correlation, a common similarity comparison algorithm. It will take the previously mentioned interaction scores and provide a correlation value representing similarity between -1 and 1.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_U8F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_U8F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png 424w, https://substackcdn.com/image/fetch/$s_!_U8F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png 848w, https://substackcdn.com/image/fetch/$s_!_U8F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png 1272w, https://substackcdn.com/image/fetch/$s_!_U8F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_U8F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png" width="1008" height="220" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/392f25d0-30a3-4677-9599-726b61895875_1008x220.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:220,&quot;width&quot;:1008,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!_U8F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png 424w, https://substackcdn.com/image/fetch/$s_!_U8F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png 848w, https://substackcdn.com/image/fetch/$s_!_U8F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png 1272w, https://substackcdn.com/image/fetch/$s_!_U8F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F392f25d0-30a3-4677-9599-726b61895875_1008x220.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The correlation is than multiplied by combined interaction scores to provide a final similarity rating.</p><p>When a user signs into MonkeyScripts, the website looks at their previously visited and downloaded scripts. From these, it will select several and generate suggestions based off of the scripts most similar to each one. These are then presented to the user as recommended scripts (with a little bit of randomness to keep things interesting).</p><p>To provide a simplified example of the result: Imagine Alice likes apples, bananas, and pears. Bob knows he likes apples and bananas. Since we know from Alice that people who like apples and bananas also like pears, we would suggest that Bob try a pear to see if he enjoys it as well. The more data points we have, the more accurate this system becomes.</p><h3>Takeaway</h3><ol><li><p>There is a lot more to making and promoting a new website than simply adding cool and modern features. SEO, advertising, content creation, etc takes different skills and a lot of time.</p></li><li><p>Simply based off of the feedback received, this project proved to me that incorporating modern learning and content discovery techniques is a feasible method of lowering the bar of entry for niche and difficult-to-understand communities.</p></li></ol><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://github.com/mwaboff/monkeyscripts&quot;,&quot;text&quot;:&quot;View Code&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://github.com/mwaboff/monkeyscripts"><span>View Code</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iQRy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iQRy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png 424w, https://substackcdn.com/image/fetch/$s_!iQRy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png 848w, https://substackcdn.com/image/fetch/$s_!iQRy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png 1272w, https://substackcdn.com/image/fetch/$s_!iQRy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iQRy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png" width="1456" height="737" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:737,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!iQRy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png 424w, https://substackcdn.com/image/fetch/$s_!iQRy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png 848w, https://substackcdn.com/image/fetch/$s_!iQRy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png 1272w, https://substackcdn.com/image/fetch/$s_!iQRy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa779f20b-5cc0-4acd-b7c9-391769b82762_1500x759.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Why you aren't learning]]></title><description><![CDATA[When "tutorial hell" and the pursuit of "mastery" prevents you from achieving your goals.]]></description><link>https://blog.aboff.com/p/why-you-arent-learning</link><guid isPermaLink="false">https://blog.aboff.com/p/why-you-arent-learning</guid><dc:creator><![CDATA[Michael Aboff]]></dc:creator><pubDate>Wed, 16 Nov 2022 08:54:45 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!yBD1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p>I finished 3 tutorials but haven&#8217;t learned anything!</p></blockquote><blockquote><p>It doesn&#8217;t stick in my head yet</p></blockquote><blockquote><p>I have been trying to learn coding for sometime now and I have always failed</p></blockquote><p>Tutorial hell is the bane of new software developers. It is the evil boogie man that hides under the bed, ever present. If you&#8217;ve ever caught yourself saying one of the above phrases, you too may have been caught in it.</p><p>You finish a tutorial, it walked you step-by-step through the process of making a tribute page, but you finish and realize you have no idea how to make a website by yourself. You feel like you just wasted days or weeks of time, maybe even some money. You might even think the problem is with yourself and that you should give up. Everyone else seems to get it, right?</p><p>No! The problem isn&#8217;t that you can&#8217;t understand how to program. It&#8217;s that you are approaching it wrong. In this article I&#8217;m going to explain my technique for escaping this pit of despair.</p><p><strong>Spoiler alert:</strong> You can <em>never</em> learn enough to feel confident. No video series will <em>ever</em> make you job ready. You can only really learn how to code by researching and experimenting.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yBD1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yBD1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png 424w, https://substackcdn.com/image/fetch/$s_!yBD1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png 848w, https://substackcdn.com/image/fetch/$s_!yBD1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png 1272w, https://substackcdn.com/image/fetch/$s_!yBD1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yBD1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png" width="728" height="296.039100684262" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/ada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:416,&quot;width&quot;:1023,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:708538,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yBD1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png 424w, https://substackcdn.com/image/fetch/$s_!yBD1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png 848w, https://substackcdn.com/image/fetch/$s_!yBD1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png 1272w, https://substackcdn.com/image/fetch/$s_!yBD1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fada3200f-cc40-4f07-84e9-ead33679b9d8_1023x416.png 1456w" sizes="100vw" loading="lazy" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Dall-e&#8217;s interpretation of &#8220;tutorial hell&#8221;, pretty apt if I say so myself!</figcaption></figure></div><h2>The Loop</h2><p>New developers are often given the recommendation to just &#8220;find a tutorial&#8221;, &#8220;buy this Udemy course&#8221;, or &#8220;complete this course&#8221;, but I want to posit that this advice misleads. Instead of leading people to self-sufficient success, it gets them stuck using training wheels. Many users on Discord servers explain to me that they complete tutorial after tutorial and still feel like they don&#8217;t know what they are doing.</p><div class="pullquote"><p>You can <em>never</em> learn enough to feel confident. No video series will <em>ever</em> make you job ready.</p></div><p>Tutorials and these software engineer YouTube influencers contribute to this issue. Just look at the titles to the courses on Udemy: </p><ul><li><p>"Complete guide to learning how to program online&#8221;</p></li><li><p>&#8220;From complete beginner to advanced. Learn Webdesign and Become a Professional Front End Web Developer!&#8221;</p></li></ul><p>These, and many other courses, are trying to convince you they have the magic secret. That you can sign up for their course and leave job ready. Instead what happens is that people complete the course, realize they have been hand-held the entire time, and have no idea how to code on their own.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IIbz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IIbz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png 424w, https://substackcdn.com/image/fetch/$s_!IIbz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png 848w, https://substackcdn.com/image/fetch/$s_!IIbz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png 1272w, https://substackcdn.com/image/fetch/$s_!IIbz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IIbz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png" width="354" height="261" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/accab97c-68a2-421c-8cf4-33769e7c06db_354x261.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:261,&quot;width&quot;:354,&quot;resizeWidth&quot;:354,&quot;bytes&quot;:12130,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IIbz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png 424w, https://substackcdn.com/image/fetch/$s_!IIbz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png 848w, https://substackcdn.com/image/fetch/$s_!IIbz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png 1272w, https://substackcdn.com/image/fetch/$s_!IIbz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faccab97c-68a2-421c-8cf4-33769e7c06db_354x261.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Tutorial hell&#8217;s endless loop</figcaption></figure></div><h2>Breaking Free</h2><p>So what is the solution? Break free of the loop by reframing what tutorials are. They shouldn&#8217;t be seen as step-by-step instructions, but rather as a guide to lead you to lead you in the generally right direction.</p><p>First, come up with an idea of a fun project that motivates you. It can be a chat bot, social media site clone, anything! This should be your guiding principle, and as you learn new concepts strongly consider how this might help you build your goal project.</p><p>If you have never programmed before, you&#8217;ll need to learn the basics. Choose a language, such as Javascript or Python, that fits best to your goal project. Free courses and textbooks are great for this, but <strong>do not feel compelled to finish them</strong>. Learn the important fundamentals, do the practice problems to reinforce the ideas, then stop wasting time.</p><p>Now is the fun part, find a tutorial that uses the same language you learned the basics in that is similar to what you are trying to accomplish as your goal project. For example, if you are trying to create a chat bot, find a tutorial on how to build a Discord chatbot with Python. If you are trying to recreate Reddit, find an HTML/CSS/Javascript tutorial on how to create a blog.</p><div class="pullquote"><p> Tutorials are not mandates from heaven, they merely point you in a general direction.</p></div><p>As you progress through the tutorial, start deviating from the instructions. Use different text, style your entries differently. Your tweaks should be small at first, then larger and larger after. You will have to start googling how to do something different until eventually you are no longer following the tutorial and just using Google to improve your creation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zQ1m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zQ1m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png 424w, https://substackcdn.com/image/fetch/$s_!zQ1m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png 848w, https://substackcdn.com/image/fetch/$s_!zQ1m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png 1272w, https://substackcdn.com/image/fetch/$s_!zQ1m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zQ1m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png" width="91" height="461" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/f185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:461,&quot;width&quot;:91,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9345,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zQ1m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png 424w, https://substackcdn.com/image/fetch/$s_!zQ1m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png 848w, https://substackcdn.com/image/fetch/$s_!zQ1m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png 1272w, https://substackcdn.com/image/fetch/$s_!zQ1m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ff185d801-9b86-48d1-8893-69fd55eaf87a_91x461.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Escape from the cycle by taking risks and deviating from the rules.</figcaption></figure></div><p></p><p>A couple things to keep in mind:</p><ul><li><p>If your goal project is a big one with many moving parts, don&#8217;t try learning all of them at once. Find a tutorial that adds one feature closer. You don&#8217;t want to go from your first Javascript functions to a frontend with React and a NodeJs backend. You&#8217;ll get there in time!</p></li><li><p>If you are finding things difficult, go through the tutorial exactly as instructed once. Then re-take the tutorial but start tweaking it towards your goal project.</p></li></ul><p>Eventually, with this iterative approach, you will be creating awesome, self-driven projects! Most importantly, as you do your own research and experiment, you will start learning much more deeply than you would following any tutorial.</p><h2>Last Words</h2><p>I don&#8217;t want you to come away from this thinking that tutorials are bad or that the people who make them are bad. Instead, feel empowered. Tutorials are not mandates from heaven, they merely point you in a general direction. It&#8217;s your responsibility to take the reins and make it work for yourself.</p><p>This is the approach I still take to this day when learning a new technology, even with a decade of experience under my belt. If it works for me, it just might work for you too!</p>]]></content:encoded></item></channel></rss>