<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
      <title>software-development on Roy Tang</title>
      <link>https://mirror.roytang.net/tags/software-development/</link>
      <description>Recent content in software-development on Roy Tang</description>
      <generator>Hugo -- gohugo.io</generator>
      <language>en-us</language>
      <managingEditor>hello@roytang.net (Roy Tang)</managingEditor>
      <webMaster>hello@roytang.net (Roy Tang)</webMaster>
      <lastBuildDate>Wed, 23 Jun 2021 13:18:54 +0000</lastBuildDate>
      
          <atom:link href="https://mirror.roytang.net/tags/software-development/index.xml" rel="self" type="application/rss+xml" />
      
          
      
        <item>
            <title>Breaking Prod
</title>
            <link>https://mirror.roytang.net/2021/06/breaking-prod/</link>
            <pubDate>Wed, 23 Jun 2021 13:18:54 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2021/06/breaking-prod/</guid>
            <description>
            
            &lt;p&gt;HBO Max caused a bit of a stir among its subscribers last week when they &lt;a href=&#34;https://www.newsweek.com/hbo-max-sparks-jokes-memes-integration-test-email-1601837&#34;&gt;accidentally sent out an email titled &amp;ldquo;Integration Test Email #1&amp;rdquo;&lt;/a&gt; to some large number of their subscribers, which spawned a lot of clever commentary and snarky remarks on Twitter. A few days later HBO Max &lt;a href=&#34;https://twitter.com/HBOMaxHelp/status/1405712235108917249&#34;&gt;sent out an apology blaming an unnamed intern for the boo-boo&lt;/a&gt;, which spawned &lt;a href=&#34;https://twitter.com/search?q=Dear%20Intern&#34;&gt;a lot of &amp;ldquo;Dear Intern&amp;rdquo; tweets&lt;/a&gt; with many commiserating with the unnamed intern by sharing their own stories of mishaps from their younger days.&lt;/p&gt;
&lt;p&gt;There was also &lt;a href=&#34;https://twitter.com/gurlcode/status/1405715883104821251&#34;&gt;a thread of other developers and their stories of how they broke production too&lt;/a&gt;. One of my favorite stories has to be from Patrick McKenzie &lt;a href=&#34;https://twitter.com/patio11/status/1405704339969220615&#34;&gt;about his worst production bug&lt;/a&gt; while he was the one-man engineering team for his startup.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a bit of an adage among programmers on the internet: &lt;a href=&#34;https://www.reddit.com/r/ProgrammerHumor/comments/jg4244/there_are_two_kinds_of_developers_those_who_have/&#34;&gt;There are two kinds of developers. Those who have broken production and those who are about to.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In my long career, I&amp;rsquo;ve either been lucky enough to have never caused any big production issues (either that or I&amp;rsquo;ve blocked out their memory somehow.) Part of it was probably because when I was a junior developer they almost never gave me access to production (like a sensible company!) Not to mention there&amp;rsquo;s always been several layers of reviews, testing and QA between me and production. And by the time I was senior enough to be handling production myself, I knew enough to be super careful with everything. Not that I didn&amp;rsquo;t work with production problems of course, I was just lucky enough to never be the direct cause of them myself. I think. But if the adage above is in any way true, should I be worried that eventually the law of averages will catch up to me, and maybe I should quite while I&amp;rsquo;m ahead?&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Thoughts on Vue.js
</title>
            <link>https://mirror.roytang.net/2021/05/thoughts-on-vue.js/</link>
            <pubDate>Sun, 23 May 2021 23:27:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2021/05/thoughts-on-vue.js/</guid>
            <description>
            
            &lt;p&gt;I&amp;rsquo;m not big on modern frontend JavaScript frameworks (mostly because I think web pages should use as little JS as possible), but when I do find the need to use one, my weapon of choice is &lt;a href=&#34;https://vuejs.org/&#34;&gt;Vue.js&lt;/a&gt;. I dislike React, but mostly because (a) my first experience with React was with mobile development using React Native for &lt;a href=&#34;https://mirror.roytang.net/2020/09/mobile-app-dev/&#34;&gt;mobile app development&lt;/a&gt;, which I generally don&amp;rsquo;t like; and (b) I don&amp;rsquo;t like Facebook, which backs React. I have no opinion on Angular.&lt;/p&gt;
&lt;p&gt;Despite being my frontend framework of choice, I don&amp;rsquo;t actually have that much experience with Vue beyond a few small projects each with a minimal number of screens / pages. That being said, I do have some thoughts, coming mostly from the POV of a developer &lt;a href=&#34;https://mirror.roytang.net/2020/02/old-web/&#34;&gt;more used to the old-school ways of Javascript and web development in general&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The entry point of course, is the &lt;a href=&#34;https://vuejs.org/v2/guide/&#34;&gt;getting started guide on the Vue website itself&lt;/a&gt;, which walks you through most of the framework features. I have no concern about most of these; the features are explained well and I find most of the documentation quite good.&lt;/p&gt;
&lt;p&gt;My issue is with how the site recommends to beginners to set up their Vue projects: via a script include from a CDN. When I first saw this guide, I assumed this was the standard, recommended way to build a Vue project, so this was how I did it for my first small project, a small demo site I needed to produce in a few days. Due to the short timeframe, I had decided to learn as I go. The component registration section of the guide also gives no guidance or recommendation on how to structure your components or projects, so I ended up with most of the components set up in a single JS file. (Admittedly, as a person with more than a decade of web dev experience, I should have at least split it up into several files, but time was short.) A technical person reviewing the code commented that I had implemented the frontend &amp;ldquo;in a very strange manner&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;It gets worse when you try to install third-party components, which is a big feature of the ecosystem of these frontend frameworks. In my experience, most third-party components will only give instructions for installation via &lt;code&gt;npm&lt;/code&gt; and a JS module system, neither of which are covered nor recommended by the beginner sections of the guide.&lt;/p&gt;
&lt;p&gt;A few projects later, and I now know that I vastly prefer developing Vue projects as Node projects built using the scaffolding provided by &lt;a href=&#34;https://cli.vuejs.org/&#34;&gt;Vue CLI&lt;/a&gt;, with the components implemented as standalone &lt;code&gt;.vue&lt;/code&gt; files. The usage of these tools and methods are covered in the &lt;a href=&#34;https://vuejs.org/v2/guide/installation.html&#34;&gt;Installation section of the guide&lt;/a&gt; (which the &amp;ldquo;Get Started&amp;rdquo; link skips over) and the &lt;a href=&#34;https://vuejs.org/v2/guide/single-file-components.html&#34;&gt;Single File Components section under the Tooling section&lt;/a&gt; (which seemed like an advanced section a beginner is not likely to get into.) This method also makes installing third-party components much more straightforward. It&amp;rsquo;s not even necessary to cover what tools like Webpack or Babel are doing, since those are largely abstracted away by the scaffolding.&lt;/p&gt;
&lt;p&gt;Basically my main complaint is that the guide doesn&amp;rsquo;t do a good job of transitioning from the introducing the beginner to the language specifics to introducing a recommended approach and tools for organizing more intermediate or complex projects. The method of using a script include seems suitable for little more than a single-page todo list webapp. I suppose the perceived shortcoming might be attributed to my background at least partially - I still tend to think like a developer using jQuery, and I don&amp;rsquo;t default to thinking about Node applications when doing webapps.&lt;/p&gt;
&lt;p&gt;That being said, I am overall happy with Vue.js and it&amp;rsquo;s still my choice of frontend framework moving forward (should a frontend framework even be needed!) Although, maybe I should at least consider trying out / evaluating Angular so I can pretend to make a fair comparison?&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>New note
</title>
            <link>https://mirror.roytang.net/2021/04/475d3f081e042d0a2b9beee29e4aa643/</link>
            <pubDate>Wed, 21 Apr 2021 14:26:20 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2021/04/475d3f081e042d0a2b9beee29e4aa643/</guid>
            <description>
            

            &lt;p&gt;Me: Been using Python since 2008&lt;/p&gt;
&lt;p&gt;Also me: Need to lookup how to use the filter function every time&lt;/p&gt;
&lt;p&gt;(Note for future me: &lt;code&gt;list = filter(func, list)&lt;/code&gt;&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Programming is hard, but anyone can be a programmer
</title>
            <link>https://mirror.roytang.net/2021/04/programming-is-hard-but-anyone-can-be-a-programmer/</link>
            <pubDate>Tue, 13 Apr 2021 05:25:58 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2021/04/programming-is-hard-but-anyone-can-be-a-programmer/</guid>
            <description>
            
            &lt;p&gt;&lt;a href=&#34;https://dorinlazar.ro/&#34;&gt;dorinlazar.ro&lt;/a&gt; talks about &lt;a href=&#34;https://dorinlazar.ro/2021-02-programming-is-hard/&#34;&gt;programming being hard&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Most beginners in programming eventually end up with the same ingratiating message: „Programming is easy, everyone can do it”, with some threatening message that people doing the gatekeeping should stop doing that. I’m here to tell you that that is not true. Programming is hard, programming is not for everyone, and for the time being everyone might be able to do it, but most definitely most should not. Like most of the empty, shallow, positive messages coming with an automatic defense against refute, in this case talking about this gatekeeping thing that simply doesn’t happen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think gatekeeping is definitely a thing that happens. I should know, I used to hold the opinion that not everybody can be a programmer, because there are non-trivial skill sets that are necessary, such as attention to detail, ability to analyze and reason out possible outcomes, the ability to imagine abstractions in your head, and so on. Back when I did training for new hires, I could easily tell who are the people who wouldn&amp;rsquo;t pass, by simply noting who had trouble with things like SQL joins. (This was back in the day when we expected our devs to be able to handle everything from backend database stuff to web frontends with HTML/CSS/JS. I assume modern day frontend devs wouldn&amp;rsquo;t have much use for SQL usage&amp;hellip;)&lt;/p&gt;
&lt;p&gt;That being said, it&amp;rsquo;s not a hard gate, where someone can tell you it&amp;rsquo;s impossible to become a programmer. Anyone can be a programmer, in the same way that anyone can become an accountant or a lawyer or such: it&amp;rsquo;s going to take a nontrivial amount of effort and hard work (I&amp;rsquo;m not claiming that being a programmer is necessarily as difficult as those jobs!). Some people may make it look easy, because they may have a bit more aptitude than others (due to background or whatever), in the same way that some people find math easy and others find it nearly impossible. And a lot of the time it&amp;rsquo;s impossible to detect whether that aptitude exists until you&amp;rsquo;re trying it out.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a huge difference between learning to code just to write a few scripts in Python or some macros in Excel vs working professionally as a programmer, possibly in a team, with serious deadlines and crunch and all that. Not everyone is suited to that life.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m all for inclusivity - we definitely need more people working in tech, and for people who are struggling, learning to code may be a way for them to live better lives. But we shouldn&amp;rsquo;t sugarcoat the challenges involved or the effort required. &amp;ldquo;Anyone can learn to code&amp;rdquo;, given enough time and effort, but that doesn&amp;rsquo;t mean programming is easy.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>HTML Tags Memory Test
</title>
            <link>https://mirror.roytang.net/2020/12/html-tags-memory-test/</link>
            <pubDate>Sun, 20 Dec 2020 16:34:25 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/12/html-tags-memory-test/</guid>
            <description>
            Shared via pocket:
    &lt;a href=&#34;https://codepen.io/plfstr/full/zYqQeRw&#34;&gt;HTML Tags Memory Test&lt;/a&gt;

            


            </description>
        </item>
    
        <item>
            <title>How Not to Mentor Software Developers
</title>
            <link>https://mirror.roytang.net/2020/12/how-not-to-mentor-software-developers/</link>
            <pubDate>Tue, 15 Dec 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/12/how-not-to-mentor-software-developers/</guid>
            <description>
            
            &lt;p&gt;I&amp;rsquo;ve talked a bit about &lt;a href=&#34;https://mirror.roytang.net/2018/10/mentoring-in-software-development/&#34;&gt;mentoring in software development&lt;/a&gt; before, and how early on I used to get feedback about me being &amp;ldquo;intimidating&amp;rdquo;. I never got any concrete feedback regarding that, so I don&amp;rsquo;t know what problems I had specifically. Though I was recently reminded of one particular incident that was a bit cringey for me personally.&lt;/p&gt;
&lt;p&gt;This was a bit more than 10 years ago I think. I was usually assigned to mentor new hires, which means fresh graduates who usually needed guidance with a Java (which was our primary programming language at that time) and with our in-house web framework, which could be a bit challenging to work with. That means a lot of my days are spent answering questions or guiding newbies through more complicated tasks.&lt;/p&gt;
&lt;p&gt;The incident I remember was with one particular newbie. After they completed a particular task, they asked me to check their work and everything was fine. I don&amp;rsquo;t have the actual wording of what I said but recall making an offhand in-jest remark congratulating them on getting an entire thing done with zero questions and no actual issues. I remember thinking almost immediately afterwards that it was a terrible thing to say since it implies that asking questions might be frowned upon. It&amp;rsquo;s kind of like the difference between just saying &amp;ldquo;Good job&amp;rdquo; and &amp;ldquo;Good job, you didn&amp;rsquo;t screw this one up!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I might have been overthinking it a bit back then as I don&amp;rsquo;t remember any actual negative reaction to that particular remark. That&amp;rsquo;s not really a good metric though, especially when you&amp;rsquo;re dealing with newbies on their first job. There&amp;rsquo;s power dynamics to consider, so they may not think it appropriate to complain even if something makes them feel bad or uncomfortable.&lt;/p&gt;
&lt;p&gt;As a mentor, it&amp;rsquo;s important to actually encourage the mentees to ask questions when things are unclear or to raise conceerns when there are problems. It&amp;rsquo;s an essential communication skill that all developers (maybe arguably all people) should have. And it&amp;rsquo;s not enough to say &amp;ldquo;Hey, you can ask me questions any time&amp;rdquo;, but you also have to reflect that openness in your actions, your feedback, and your comments.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Server Setup Notes: Ubuntu, Nginx, Django, MySQL
</title>
            <link>https://mirror.roytang.net/2020/11/server-setup-notes/</link>
            <pubDate>Thu, 05 Nov 2020 06:59:41 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/11/server-setup-notes/</guid>
            <description>
            
            &lt;p&gt;I recently did &lt;a href=&#34;https://mirror.roytang.net/2020/10/site-migration-to-django/&#34;&gt;a server migration&lt;/a&gt; since I moved to new hosting, The move was from managed/shared hosting to a VPS, these are some notes I took during the process, which I figure might be helpful if I ever tried to  do this again. (And maybe someone else finds it helpful too). Links and references to helpeful resources are included.&lt;/p&gt;
&lt;h3 id=&#34;setting-up-a-webserver-and-wsgi-container&#34;&gt;Setting up a webserver and WSGI container&lt;/h3&gt;
&lt;p&gt;I already knew I wanted to use Nginx (managed hosting on the old server always used Apache), that meant needing to choose a WSGI container for the Django apps. The choices were either gunicorn or uwsgi. Readings suggest minimal performance difference between the two. I did try setting up both, and found uwsgi&amp;rsquo;s setup much more straightforward and with more helpful step-by-step documentation: &lt;a href=&#34;https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html&#34;&gt;https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Nginx itself was pretty straightforward to set up. This &amp;ldquo;common pitfalls&amp;rdquo; article was helpful in resolving issues: &lt;a href=&#34;https://origin-www.wp.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/&#34;&gt;https://origin-www.wp.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One thing I will note that&amp;rsquo;s not in the article above is that for static files to be served by Nginx, it was easier to just have everything be under /var/www, for permission purposes. I initially had static files in a different folder in my user root, but really there&amp;rsquo;s no reason for that since you&amp;rsquo;re going to serve them statically anyway.&lt;/p&gt;
&lt;h3 id=&#34;setting-up-php-on-nginx&#34;&gt;Setting up PHP on Nginx&lt;/h3&gt;
&lt;p&gt;I needed PHP since I had a few minor scripts/pages and a Wordpress install that needed PHP. For the setup on Nginx, I found this guide from askubuntu super helpful: &lt;a href=&#34;https://askubuntu.com/questions/134666/what-is-the-easiest-way-to-enable-php-on-nginx&#34;&gt;https://askubuntu.com/questions/134666/what-is-the-easiest-way-to-enable-php-on-nginx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This guide from DigitalOcean was also helpful: &lt;a href=&#34;https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-in-ubuntu-16-04&#34;&gt;https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-in-ubuntu-16-04&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I was encountering some issues with fastcgi not mapping requests to the correct PHP script if the Nginx location block was aliased. This was with me using the default &lt;code&gt;snippets/fastcgi-php.conf&lt;/code&gt; that was provided with the fastcgi install. I resolved the issue by adding an additional fastcgi_param to the php location block like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    location /sample-path {
        alias /var/www/sample-path;
        # autoindex on;

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_param  SCRIPT_FILENAME    $request_filename;
                fastcgi_pass unix:/run/php/php7.4-fpm.sock;
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The solution for this came from the Nginx common pitfalls article listed above.&lt;/p&gt;
&lt;h3 id=&#34;wordpress-specific&#34;&gt;Wordpress-specific&lt;/h3&gt;
&lt;p&gt;(Added 1/21/2021)&lt;/p&gt;
&lt;p&gt;Wordpress worked well enough for the most part. Except permalinks didn&amp;rsquo;t work! I had to add a directive to the nginx config file for unknown urls to be redirected to index.php:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;try_files $uri $uri/ /index.php?$args;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Source: &lt;a href=&#34;https://nginxlibrary.com/wordpress-permalinks/&#34;&gt;https://nginxlibrary.com/wordpress-permalinks/&lt;/a&gt;. It&amp;rsquo;s also mentioned in the &lt;a href=&#34;https://wordpress.org/support/article/nginx/&#34;&gt;official Wordpress docs&lt;/a&gt; , but buried in a bunch of other stuff so less helpful.&lt;/p&gt;
&lt;h3 id=&#34;choosing-a-database&#34;&gt;Choosing a database&lt;/h3&gt;
&lt;p&gt;I initially wanted to use PostgreSQL (since that&amp;rsquo;s what I was using locally) and set that up first. Then I realized I had a Wordpress install I wanted to carry over, and apparently Wordpress needs MySQL, so oops. I didn&amp;rsquo;t want to have two databases running, so that was out. Plan out your database usage boys.&lt;/p&gt;
&lt;h3 id=&#34;setting-up-mysql-for-django&#34;&gt;Setting up MySQL for Django&lt;/h3&gt;
&lt;p&gt;I created the initial MySQL database with character set &lt;code&gt;utf8&lt;/code&gt;, which apparently had the dumb implementation of only supporting 3 bytes for multibyte characters. This led to errors when I tried importing records into Django that had emojis and the like in text. Apparently the &amp;ldquo;correct&amp;rdquo; character set to use is &lt;code&gt;utf8mb4&lt;/code&gt; (mb4 -&amp;gt; &amp;ldquo;multibyte 4&amp;rdquo; I guess?). It&amp;rsquo;s kinda dumb that they had to invent a &amp;ldquo;more utf8 character set&amp;rdquo; because of their initial dumb implementation. It&amp;rsquo;s also dumb that I didn&amp;rsquo;t realize this when apparently the MySQL databases on the old server were already using &lt;code&gt;utf8mb4&lt;/code&gt;! On the Django side, you need to specify the charset in the database settings. Helpful for resolving this issue was donturner&amp;rsquo;s answer on this SO question: &lt;a href=&#34;https://stackoverflow.com/questions/2108824/&#34;&gt;https://stackoverflow.com/questions/2108824/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After some initial testing, I also encountered a &amp;ldquo;MySQL has gone away&amp;rdquo; every so often. Turns out Django was holding on to connections for too long, so a timeout had to be set in the database settings as well. SO reference: &lt;a href=&#34;https://stackoverflow.com/questions/26958592/&#34;&gt;https://stackoverflow.com/questions/26958592/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Edit: I just encountered this a few hours after I wrote this post, when I tried to make the site display all times in Manila local time. Apparently, even if I set my Django app to be timezone, aware, additional setup needs to be done on the MySQL side so it knows what the actual timezones are (feels like they should really do this as part of the install!). Basically you need to run &lt;code&gt;mysql_tzinfo_to_sql&lt;/code&gt; to load the OS timezone info into the database. This is in the &lt;a href=&#34;https://django.readthedocs.io/en/stable/ref/databases.html&#34;&gt;Django documentation&lt;/a&gt; and the &lt;a href=&#34;https://dev.mysql.com/doc/refman/5.6/en/mysql-tzinfo-to-sql.html&#34;&gt;MySQL documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;misc-stuff&#34;&gt;Misc stuff&lt;/h3&gt;
&lt;p&gt;I used cron to set up most of the batch jobs I usually run (like the Twitter bots, etc). Initially I had problems where it seemed like cron wasn&amp;rsquo;t running the jobs, even though syslog showed me the cron daemon was actually running. I don&amp;rsquo;t have notes on how I resolved this, but mentioning it here so that if this happens again in the future, I know to actuall take notes!&lt;/p&gt;
&lt;p&gt;Setting up log files for my cron jobs: I did it this way: &lt;code&gt;0 * * * * /path-to-batch/hourly.sh &amp;gt;&amp;gt; /var/log/cron/my-log-file.log 2&amp;gt;&amp;amp;1&lt;/code&gt;. The &amp;ldquo;2&amp;gt;&amp;amp;1&amp;rdquo; at the end is necessary so that stderr is redirected to the same file (otherwise you&amp;rsquo;d only get stdout).&lt;/p&gt;
&lt;p&gt;Log rotation: these is the kind of thing you don&amp;rsquo;t realize you need to do when you&amp;rsquo;re managing your own server. Anyway, I found this guide helpful: &lt;a href=&#34;https://linoxide.com/linux-how-to/setup-log-rotation-logrotate-ubuntu/&#34;&gt;https://linoxide.com/linux-how-to/setup-log-rotation-logrotate-ubuntu/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I needed to set up AWStats on the new server (and migrate data from the old server). This guide was helpful: &lt;a href=&#34;https://larsee.com/blog/2020/06/awstats-on-ubuntu-20-04-with-nginx/&#34;&gt;https://larsee.com/blog/2020/06/awstats-on-ubuntu-20-04-with-nginx/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When migrating the old AWStats data files, the filename format from the oldserver was slightly different (new install needed to end with &lt;code&gt;*.roytang.net.txt&lt;/code&gt;), so I learned a bit of bash scripting to do the batch rename:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;for i in /path-to-tmp/awstats/*.txt ; do cp &amp;quot;$i&amp;quot; &amp;quot;/path-to-tmp/awstats/out/$(basename &amp;quot;$i&amp;quot; .txt).roytang.net.txt&amp;quot; ; done&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Setting up Let&amp;rsquo;s Encrypt SSL on Nginx: &lt;a href=&#34;https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/&#34;&gt;https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;lessons-learned&#34;&gt;Lessons learned&lt;/h3&gt;
&lt;p&gt;One thing I didn&amp;rsquo;t expect from the migration from shared hosting to a VPS is how much memory I&amp;rsquo;d need. My old hosting plan was only on 512MB ram, and the lowest DigitalOcean droplet had 1GB RAM, so I figured that was fine! But MySQL occasionally had out of memory errors on startup even at 1GB ram, so I bumped up to the next level (2GB RAM). I guess the database RAM usage didn&amp;rsquo;t count against my limits on the shared hosting! The next tier isn&amp;rsquo;t much more expensive and is still manageable for my casual/hobby hosting purposes, but I will probably try to look into more detail into the memory situation at some point to see what I can optimize.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>On Mobile App Development
</title>
            <link>https://mirror.roytang.net/2020/09/mobile-app-dev/</link>
            <pubDate>Thu, 03 Sep 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/09/mobile-app-dev/</guid>
            <description>
            
            &lt;p&gt;This post is just quite a few thoughts on mobile apps and mobile app development, all mishmashed together. I don&amp;rsquo;t claim to be a mobile app specialist, at best I&amp;rsquo;ve &lt;em&gt;dabbled&lt;/em&gt; in them, but enough to form some opinions I guess?&lt;/p&gt;
&lt;h3 id=&#34;a-bit-of-history&#34;&gt;A Bit of History&lt;/h3&gt;
&lt;p&gt;My first exposure to mobile app development when I got pulled to help my then-company&amp;rsquo;s then-fledgling mobile team with cleaning up the codebase for their iOS app. This was back in maybe 2011? It was one of those projects where some devs built a quick proof-of-concept demo using new technology, then management liked it and asked for new features and they kept adding new features haphazardly until there was just this huge unmaintainable mess of code. This isn&amp;rsquo;t a knock against Objective-C or iOS development specifically or the dev team; the fact is they were new to the stack and were learning as they went while still trying to meet deadlines. I also spent some time reviewing the corresponding Android team&amp;rsquo;s code as well. This was the bulk of my exposure to native mobile dev on iOS/Android. (This was before Swift, so I never got to experience that. Language looks neat though.)&lt;/p&gt;
&lt;p&gt;Coming from a web dev background, I&amp;rsquo;ve always found the fragmentation of platforms annoying. Basically your effort gets multiplied by the number of platforms you have to support, and what would work well on iOS may not work well on Android and vice versa.&lt;/p&gt;
&lt;p&gt;Later on, this concern was alleviated a bit with the rise of common frameworks that could publish to both iOS and Android. The first one I tried of these was React Native. I tried it for a small mobile project I did around 3-ish years ago. I wasn&amp;rsquo;t tremendously happy with the experience. My mistake was probably diving head-first into React Native, when this was my first experience with React and front-end frameworks in general (something new to someone with &lt;a href=&#34;https://mirror.roytang.net/2020/02/old-web/&#34;&gt;my jQuery background&lt;/a&gt;!), so a lot of concepts were new to me.&lt;/p&gt;
&lt;p&gt;To make matters worse, the React Native docs at that time seemed to assume some familiarity already with React, or at least similar frameworks like Angular. I had a bit of confusion with whether I should be using Expo or not, and how to eject, and it resulted in having to restart the project a couple of times. It was also my first encounter with NPM&amp;rsquo;s dependency management, and that annoyed me a lot. A lot of the third-party dependencies I used back then were still immature too, so I often had problems with things like accessing the camera roll and so on.&lt;/p&gt;
&lt;p&gt;Nevertheless, it was experience, and the ability to publish cross-platform to iOS and Android was invaluable. I went on to use React Native as my go-to mobile framework I would recommend for other projects afterwards.&lt;/p&gt;
&lt;p&gt;Recently, I had to do a UI revamp for that small mobile app I wrote three years ago, and I hadn&amp;rsquo;t touched it for a while so I had some trouble with NPM trying to get it to run. In frustration, I gave up on it and decided to rewrite it in Flutter, of which I&amp;rsquo;ve heard good things. Flutter turned out to be great, and it took me just a little over two weeks, part-time to rewrite the app, probably slightly less effort than it took me to write it in the first place. I don&amp;rsquo;t have enough experience yet to have a detailed review of Flutter, but I was generally much happier with the experience compared to React. Part of that may be I&amp;rsquo;m much more familiar with the mobile landscape now and have a better idea of what I&amp;rsquo;m doing, but I really did encounter much fewer points of friction and Flutter seems to have much better documentation. I believe it will be my first choice of frameworks moving forward.&lt;/p&gt;
&lt;h3 id=&#34;not-everything-needs-to-be-a-native-app&#34;&gt;Not Everything Needs to Be a Native App&lt;/h3&gt;
&lt;p&gt;One of the things that annoys me about mobile apps is that people/companies unnecessarily want things to be native mobile apps when they would work perfectly fine as web apps. I can understand applications such as Uber may need numerous native functionality (GPS, timely notifications, offline functionality), so that&amp;rsquo;s fine (although many of those things are available using modern browser APIs as well!). Things like online newspapers, forums like Reddit, and so on, are perfectly fine as normal web apps, and should stop prompting me to install their native apps when they detect I am on mobile. Granted that you may get a much better UX experience with a native app, but you also have to install it locally and grant it more access than what the browser&amp;rsquo;s sandbox provides, and that feels like a tradeoff decision that the user needs to make, and not something to nag the user about constantly.&lt;/p&gt;
&lt;p&gt;I guess part of it is that I grew up with the open web and have a significant bias towards it. In general, if I can just access an app via web, I will prefer that, unless the native app experience is vastly superior or otherwise necessary. And this focus on native apps can be bad for the web. This is apparent in apps like Instagram, which don&amp;rsquo;t allow you to use their primary function - uploading images - on a desktop browser. I also had a similar discussion with a freelance client a few years ago, when they wanted to make a mobile app, but not a corresponding web platform, when all of the app&amp;rsquo;s functionality was simple stuff that web could do easily.&lt;/p&gt;
&lt;h3 id=&#34;different-platforms&#34;&gt;Different Platforms&lt;/h3&gt;
&lt;p&gt;The differences in iOS and Android are particularly annoying to me as a developer, especially since I&amp;rsquo;m used to web development. With web, everything is open and standards-based, so you generally write everything using a single stack, and any reasonably standards-compliant web browser will work with the web app in a reasonably consistent way. (Granted, there will be browser-specific issues, but nowadays those aren&amp;rsquo;t too common.) With iOS and Android, you have to test on both platforms extensively, and the build processes and distribution methods are different for each. Frameworks like React Native and Flutter lighten this load considerably, but you still really have to devote more time and resources to testing on the different platforms.&lt;/p&gt;
&lt;h3 id=&#34;backward-compatibility&#34;&gt;Backward Compatibility&lt;/h3&gt;
&lt;p&gt;Okay, this one may be an unfounded concern of mine since I don&amp;rsquo;t have much experience in the matter, but I have this worry that native mobile apps tend to &lt;em&gt;rot&lt;/em&gt; more easily, and by &lt;em&gt;rot&lt;/em&gt; I mean, get worse with time, as the mobile OSes upgrade themselves very rapidly. Unlike, say, Windows, which gets a new major version every few years, iOS and Android are constantly getting new versions and more importantly, deprecating APIs and adding new ones! Hence my worry that unmaintained, a perfectly functional app may become unworkable a few years down the line and no longer work on the latest mobile OSes. It&amp;rsquo;s my totally unfounded belief that this is a significant reason why &lt;a href=&#34;https://mirror.roytang.net/2020/07/itunes-purchases/&#34;&gt;more than 50% of my lifetime App Store purchases are no longer listed on the App Store&lt;/a&gt;. (And also why I&amp;rsquo;ve pretty much disavowed making mobile purchases again.) Say what you will about Windows, but at least they have a pretty good record of backward compatibility when software I&amp;rsquo;ve written for Windows in the early 2000s is still very likely to run on Windows today.&lt;/p&gt;
&lt;h3 id=&#34;closed-platforms&#34;&gt;Closed Platforms&lt;/h3&gt;
&lt;p&gt;Another issue I have with native apps is that I have been used to working on open platforms. Windows, for all its faults, is still an open platform, and anyone can write software for it and distribute it using whatever method they desire. And the web is even more open and standards-based (for now at least). For mobile, Android is mostly open, but iOS is mostly a closed platform.&lt;/p&gt;
&lt;p&gt;Apple&amp;rsquo;s tight control over the iOS platform has been in the tech news recently. First was &lt;a href=&#34;https://www.theverge.com/2020/6/18/21296180/apple-hey-email-app-basecamp-rejection-response-controversy-antitrust-regulation&#34;&gt;the kerfuffle with hey.com&lt;/a&gt; a few months ago, and more recently, &lt;a href=&#34;https://www.macrumors.com/guide/epic-games-vs-apple/&#34;&gt;their removal of Fortnite and battle with Epic&lt;/a&gt; due to Epic&amp;rsquo;s objection to Apple taking a cut of all purchases on iOS.&lt;/p&gt;
&lt;p&gt;As a mobile developer, the most annoying things about Apple&amp;rsquo;s tight control over iOS apps are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;that $100 annual fee for an Apple Developer account. If you&amp;rsquo;re a 1st world developer, that cost isn&amp;rsquo;t much, but if you&amp;rsquo;re a young hobbyist developer in a poorer country who just wants to try making an app, it can be a significant hurdle. Google also charges you some token amount for Play Store access, but at least it&amp;rsquo;s just a one-time fee.&lt;/li&gt;
&lt;li&gt;the fact that you need to use a Mac to publish apps to the App Store. This is another significant hurdle for hobbyist developers; Macs are typically very expensive computers. Granted Android development requires a beefy setup too, but at least you get a choice of platform for Android development.&lt;/li&gt;
&lt;li&gt;App store review can be unpredictable; sometimes things that were perfectly fine during a previous release are now flagged in the next review. It feels like you need to get lucky with the reviewer you get. And if the reviewer is unfair to your app, you may be out of luck unless you&amp;rsquo;re a big company like Epic or &lt;a href=&#34;https://www.digitaltrends.com/news/wordpress-developers-says-apple-wants-30-percent-of-its-profits-from-app-store/&#34;&gt;Wordpress that can raise a stink about it&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many of the issues that could be levied against Apple for their monopoly over the App Store could be largely addressed if the platform allowed users to install apps via alternative methods, the same way Android does. I fully understand that Apple&amp;rsquo;s tight controls enable a safer environment for naive end users (presumably, assuming they don&amp;rsquo;t screw up), and I actually approve some of their controls like their iOS14 plans to restrict targetted advertising. But more advanced users should be given the option to use alternative methods if they so choose. They can still flag apps installed via alternative methods as &amp;ldquo;potentially unsafe&amp;rdquo; and add hurdles to prevent less capable users from being easily tricked by malicious actors, (Android does this, I believe).&lt;/p&gt;
&lt;h3 id=&#34;in-conclusion&#34;&gt;In conclusion&lt;/h3&gt;
&lt;p&gt;I don&amp;rsquo;t really have a point here. Maybe I just want everything to be open? I like open platforms lol.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>On Mozilla and Firefox
</title>
            <link>https://mirror.roytang.net/2020/08/on-mozilla-and-firefox/</link>
            <pubDate>Tue, 18 Aug 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/08/on-mozilla-and-firefox/</guid>
            <description>
            
            &lt;p&gt;Mozilla made the tech news recently for laying off a whole lot of people. (&lt;a href=&#34;https://blog.mozilla.org/blog/2020/08/11/changing-world-changing-mozilla/&#34;&gt;Official statement&lt;/a&gt;). People were alarmed and worried about the future of what is the last major independent browser and the open web, bit it looks like it isn&amp;rsquo;t that bleak. Most of the layoffs were to teams other than those working on Firefox, things like the experimental browser engine Servo, devtools, and MDN. The core Gecko team seems to be unaffected.&lt;/p&gt;
&lt;p&gt;Not that these things aren&amp;rsquo;t important. MDN, if you&amp;rsquo;re not familiar, is a set of documentation of web standards and browser support, &lt;a href=&#34;https://developer.mozilla.org/en-US/&#34;&gt;available online&lt;/a&gt;, that is one of the best resources for web developers working on cross-platform things. Mozilla no longer funding it&amp;rsquo;s maintenance is a big deal, it means it&amp;rsquo;s less likely to be kept up-to-date, even though community work can still continue. Peter-Paul Koch, writer of &lt;a href=&#34;https://quirksmode.org&#34;&gt;quirksmode.org&lt;/a&gt;, which was the original resource for cross-browser compatibility back in the heyday of the browser wars, wrote an article about Mozilla&amp;rsquo;s problems titled &lt;a href=&#34;https://quirksmode.org/blog/archives/2020/08/the_cult_of_the.html&#34;&gt;&amp;ldquo;The Cult of the Free&amp;rdquo;&lt;/a&gt; where he argues that it&amp;rsquo;s time to step out of this mentality that all these resources must be maintained for free by &amp;ldquo;the community&amp;rdquo;, but that we as consumers must get used to the idea of paying or donating to support these resources. Expecting &amp;ldquo;the community&amp;rdquo; to step in and maintain these resources &lt;a href=&#34;https://www.quirksmode.org/blog/archives/2020/08/i_love_mdn_or_t.html&#34;&gt;also devalues the work of technical writers.&lt;/a&gt; Trying to get more people to support these &amp;ldquo;free&amp;rdquo; resources is a good idea, but I worry it goes against the grain of years of internet culture which has conditioned people to expect online content for free.&lt;/p&gt;
&lt;p&gt;Speaking of contributing, a while back I had considered looking for open source projects I could contribute to, and preferred projects that I actively use. Given that I&amp;rsquo;d been using Firefox &lt;a href=&#34;https://mirror.roytang.net/2004/10/firefox-1-0-on-november-9/&#34;&gt;since before version 1.0&lt;/a&gt;, one of the projects I considered was Firefox. I went through the whole rigmarole of &lt;a href=&#34;https://firefox-source-docs.mozilla.org/setup/windows_build.html&#34;&gt;their instructions to set up my machine for the Windows build&lt;/a&gt;, and just the setup process, which involved setting up and checking out code took more than an hour. That was fine for initial setup, but actually building and running Firefox itself took an additional hour or so. Might have been more than that even, it was a while back so I don&amp;rsquo;t clearly remember, but I do remember thinking that I didn&amp;rsquo;t have time for these hour-long build times. The whole set up also took up a nontrivial amount of my SSD disk space (imagine how much worse the build time would have been if I had used my non-SSD drive!), which I am always dangerously low on. These 2 factors led me to shelve the idea for the time being; perhaps I could revisit the idea of contributing to Firefox when I upgraded my computer or had more SSD space available. (TBF, I kind of gave up real quick after the first build, maybe the builds get faster on succeeding runs?). So that&amp;rsquo;s my personal &amp;ldquo;contributing-to-Firefox&amp;rdquo; story, for now.&lt;/p&gt;
&lt;p&gt;The sheer size of the codebase and the long build times just drive home the point of how much modern browsers are these large complex beasts that are doing so much. They are pretty much an OS of their own since they host a ton of features and web applications and JIT JS and all of that. This means it&amp;rsquo;s really difficult for independent browser efforts to hit feature parity with modern web browsers - even just working with JS and CSS standards, there are so many to implement.&lt;/p&gt;
&lt;p&gt;Drew DeVault laments that &lt;a href=&#34;https://drewdevault.com/2020/08/13/Web-browsers-need-to-stop.html&#34;&gt;Web browsers need to stop&lt;/a&gt; with all these new functionalities and APIs that add scope and complexity, and maybe focus more on performance, efficiency, etc. I do agree - if I was contributing to Firefox, I would want to find a way to reduce the massive memory footprint it always has. I don&amp;rsquo;t even use that many tabs, and I already feel burdened by Firefox&amp;rsquo;s memory footprint. (Pretty sure Chrome is pretty bad in this regard as well.)&lt;/p&gt;
&lt;p&gt;Why care about Mozilla/Firefox at all? Why not just use Chrome, it&amp;rsquo;s pretty good right?&lt;/p&gt;
&lt;p&gt;First of all, no, Chrome is not necessarily &amp;ldquo;pretty good&amp;rdquo;, given how it props up a surveillance capitalsm engine.&lt;/p&gt;
&lt;p&gt;Secondly, even if you didn&amp;rsquo;t dislike Google, having a browser (engine) monoculture is not a good thing. I &lt;a href=&#34;https://mirror.roytang.net/2020/02/old-web/&#34;&gt;got my start in web development&lt;/a&gt; at around the height of Internet Explorer 6&amp;rsquo;s dominance, and I&amp;rsquo;d rather not find my way back there. Granted, one of the issues back then was that IE6 was a stagnant platform that upstarts Firefox and eventually Chrome left behind. No such problem exists now, since Chrome is still in active development and still pushing new web standards forward. And the fact that Chrome is an open-source platform means in theory someone could just fork it if ever Google goes off the rails or such. (And people have already built indie browsers on top of Chrome of course!) But it&amp;rsquo;s still dangerous for a single company and/or codebase to be the sole major implementer of web standards. A single company dictating the future of the web isn&amp;rsquo;t a good thing, and is the very opposite of the idea of an &amp;ldquo;open&amp;rdquo; web.&lt;/p&gt;
&lt;p&gt;Side note: Maybe I should try building Chromium too, just for comparison.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s all my ranting for today. In conclusion: reduce your surveillance capitalism footprint and support the open web, use Firefox.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Using Hugo to Automatically Update Your Github Profile
</title>
            <link>https://mirror.roytang.net/2020/07/hugo-update-github-profile/</link>
            <pubDate>Sat, 11 Jul 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/07/hugo-update-github-profile/</guid>
            <description>
            
            &lt;p&gt;So recently Github secretly rolled out a new feature where you can create a file named &lt;code&gt;README.md&lt;/code&gt; in a repo named &lt;code&gt;github.com/&amp;lt;your github username&amp;gt;/&amp;lt;your github username&amp;gt;&lt;/code&gt;, and that markdown file would be rendered on your Github profile page. It means, you can now put basically anything you want on your Github profile!&lt;/p&gt;
&lt;p&gt;I just read this &lt;a href=&#34;https://simonwillison.net/2020/Jul/10/self-updating-profile-readme/&#34;&gt;post from Simon Willson&lt;/a&gt; about using this new feature + Github actions and a Python script to automatically generate and update his Github profile. That seemed neat so I thought about how I would do it.&lt;/p&gt;
&lt;p&gt;My blog is already setup to publish using &lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt; and &lt;a href=&#34;https://travis-ci.org/&#34;&gt;Travis-CI&lt;/a&gt; whenever I push a commit, so I figured I could just ride on that framework. You can already see this in action on &lt;a href=&#34;https://github.com/roytang&#34;&gt;my Github profile page&lt;/a&gt;! Basically, I publish the &lt;code&gt;README.md&lt;/code&gt; using my regular Hugo build, then my Travis script will deploy it to the appropriate repo.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re new to Hugo, the trickiest part of this might be using a custom output format to generate the README.md. This is basically just a bunch of settings in your Hugo config file:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Define a new Markdown media type under &lt;code&gt;mediaTypes&lt;/code&gt; in your config file. For a TOML config file, that looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;    [&lt;span style=&#34;color:#a6e22e&#34;&gt;mediaTypes&lt;/span&gt;]
        [&lt;span style=&#34;color:#a6e22e&#34;&gt;mediaTypes&lt;/span&gt;.&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/markdown&amp;#34;&lt;/span&gt;]
            &lt;span style=&#34;color:#a6e22e&#34;&gt;suffixes&lt;/span&gt; = [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;md&amp;#34;&lt;/span&gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Define a &amp;ldquo;readme&amp;rdquo; output format (or whatever you want to name it) under &lt;code&gt;outputFormats&lt;/code&gt; in your config file. For a TOML config file, that looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;outputFormats&lt;/span&gt;]
  [&lt;span style=&#34;color:#a6e22e&#34;&gt;outputFormats&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readme&lt;/span&gt;]
    &lt;span style=&#34;color:#a6e22e&#34;&gt;baseName&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;README&amp;#34;&lt;/span&gt;
    &lt;span style=&#34;color:#a6e22e&#34;&gt;mediaType&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/markdown&amp;#34;&lt;/span&gt;    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;baseName&lt;/code&gt; here controls the output file name, while the extension is controlled by the suffixes in step 1. The &lt;code&gt;mediaType&lt;/code&gt; you use here should match the one in step&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next is to specify which pages should have this custom output format. I used my home page for this, so under &lt;code&gt;outputs&lt;/code&gt; in the config file I added the name of the output format (&amp;ldquo;readme&amp;rdquo;) in my case under &amp;ldquo;home&amp;rdquo;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;outputs&lt;/span&gt;]
  &lt;span style=&#34;color:#a6e22e&#34;&gt;home&lt;/span&gt; = [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HTML&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;RSS&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;jsonfeed&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hfeed&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;readme&amp;#34;&lt;/span&gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Last step, in your theme or your layouts folder, create a file named &lt;code&gt;index.README.md&lt;/code&gt;. This will be the template used to generate the README file. Remember that this is a template to generate markdown. You can use all the normal Hugo template functions, but your output should be markdown. Here&amp;rsquo;s part of what I put in my template, which generates a list of my most recent blog posts:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-go-html-template&#34; data-lang=&#34;go-html-template&#34;&gt;Latest blog posts:
&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;$pages&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;first&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;where&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;site&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.RegularPages&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Type&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;    
&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;$pages&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;
- [&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.Title&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;](&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.Permalink&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;)&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;

[View all posts](&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.Permalink&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;blog)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you&amp;rsquo;re not yet too familiar with Hugo templates, you can maybe just use the above example directly first to see what it outputs. (You might need to replace &lt;code&gt;post&lt;/code&gt; with &lt;code&gt;posts&lt;/code&gt; or whatever your main post type is).&lt;/p&gt;
&lt;p&gt;Once you have these setup, just run your usual hugo build and check the output folder to see if the README.md file was generated correctly. It should be in the root of the output folder, same place as the &lt;code&gt;index.html&lt;/code&gt; for your home page. One problem I encountered with Hugo is that it insists on lowercasing all the output files, so the output file in my case is always &lt;code&gt;readme.md&lt;/code&gt; and not &lt;code&gt;README.md&lt;/code&gt;. I just solved this by renaming it during the Travis build.&lt;/p&gt;
&lt;p&gt;I won&amp;rsquo;t discuss the travis deployment process anymore here; if you&amp;rsquo;ve done CI with Travis and Github integration before, it&amp;rsquo;s relatively straightforward to add a step to copy the readme file from the Hugo build output to the appropriate repository.&lt;/p&gt;
&lt;p&gt;After setting this up, my Github profile page now automatically updates with my latest blog posts/notes/shared links! (I didn&amp;rsquo;t include photos to keep things simple). The update happens every time my site itself gets a new build, so that&amp;rsquo;s pretty neat. I&amp;rsquo;ve attached an image of the new Github profile page for reference!&lt;/p&gt;



&lt;img src=&#34;https://mirror.roytang.net/2020/07/hugo-update-github-profile/sample_hucb5e7c9f1f7a14a92d223160e1fe09f1_359089_300x0_resize_box_2.png&#34; /&gt;



            </description>
        </item>
    
        <item>
            <title>Image Lightboxes using HTML/CSS
</title>
            <link>https://mirror.roytang.net/2020/07/image-lightboxes-using-html/css/</link>
            <pubDate>Wed, 01 Jul 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/07/image-lightboxes-using-html/css/</guid>
            <description>
            
            &lt;p&gt;In several places on this site (like if you click &lt;a href=&#34;https://mirror.roytang.net/photos/&#34;&gt;Photos&lt;/a&gt; in the menu up top), I have a grid-like view of a list of photos/images:&lt;/p&gt;











&lt;a href=&#34;#faccc892f8e2cf4a4264858849ce5ae2-lightbox&#34;&gt;
    &lt;figure&gt;
      &lt;img src=&#34;https://mirror.roytang.net/2020/07/image-lightboxes-using-html/css/photos_hua697e57b9c5f61f8be116df80d63f53b_922940_300x0_resize_box_2.png&#34; alt=&#34;&#34; title=&#34;&#34; class=&#34;tn&#34; /&gt;
      &lt;figcaption&gt; (Click to view full-size)&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/a&gt;
&lt;div class=&#34;lightbox&#34; id=&#34;faccc892f8e2cf4a4264858849ce5ae2-lightbox&#34; style=&#34;display: none;&#34;&gt;
  &lt;a href=&#34;#_&#34;&gt;
    &lt;img src=&#34;https://mirror.roytang.net/2020/07/image-lightboxes-using-html/css/photos.png&#34; /&gt;
  &lt;/a&gt;
  &lt;div class=&#34;lightbox_overlay&#34;&gt;
    &lt;p&gt;&lt;/p&gt;
    &lt;time class=&#34;dt-published&#34; datetime=&#34;1 Jul 2020 12:00am&#34;&gt;1 Jul 2020 12:00am&lt;/time&gt;&lt;a href=&#34;#_&#34;&gt;Close&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;I used to just have each thumbnail open the post permalink on click, with the anchor set to the image itself. The image would be shown in full size inline of the post. This was a bit clunky and not so modern, so I decided to implement it so that the image lists instead will show a &lt;a href=&#34;https://en.wikipedia.org/wiki/Lightbox_%28JavaScript%29&#34;&gt;Lightbox&lt;/a&gt;-style overlay with the full image and some details and an option to click through to the post. That looks a little something like this:&lt;/p&gt;











&lt;a href=&#34;#69db43a1058dcdddf90179b43e7ecf6d-lightbox&#34;&gt;
    &lt;figure&gt;
      &lt;img src=&#34;https://mirror.roytang.net/2020/07/image-lightboxes-using-html/css/lightbox_hu396d03ff45b11c54616c22d72e1875ca_532641_300x0_resize_box_2.png&#34; alt=&#34;&#34; title=&#34;&#34; class=&#34;tn&#34; /&gt;
      &lt;figcaption&gt; (Click to view full-size)&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/a&gt;
&lt;div class=&#34;lightbox&#34; id=&#34;69db43a1058dcdddf90179b43e7ecf6d-lightbox&#34; style=&#34;display: none;&#34;&gt;
  &lt;a href=&#34;#_&#34;&gt;
    &lt;img src=&#34;https://mirror.roytang.net/2020/07/image-lightboxes-using-html/css/lightbox.png&#34; /&gt;
  &lt;/a&gt;
  &lt;div class=&#34;lightbox_overlay&#34;&gt;
    &lt;p&gt;&lt;/p&gt;
    &lt;time class=&#34;dt-published&#34; datetime=&#34;1 Jul 2020 12:00am&#34;&gt;1 Jul 2020 12:00am&lt;/time&gt;&lt;a href=&#34;#_&#34;&gt;Close&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Now, in the olden days doing this sort of thing required JavaScript (as described in the wikipedia link above to Lightbox). But for the modern web, this can be achieved using HTML and CSS only. The method is described in &lt;a href=&#34;https://jlelse.blog/dev/css-lightbox-hugo/&#34;&gt;this blog post by jlelse&lt;/a&gt;, specifically for use in Hugo themes. The CSS technique is relatively straightforward: you generate two images, a thumbnail, and a hidden full-size one in an overlay. The thumbnail is wrapped in an anchor pointing to the id of the hidden overlay, and the overlay gets displayed via the &lt;code&gt;:target&lt;/code&gt; CSS pseudo-selector.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how that looks in this site&amp;rsquo;s current Hugo templates:&lt;/p&gt;
&lt;p&gt;( &lt;code&gt;.context&lt;/code&gt; is the original image resource, and &lt;code&gt;$thumb&lt;/code&gt; is the generated thumbnail)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-go-html-template&#34; data-lang=&#34;go-html-template&#34;&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.context.RelPermalink&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;md5&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-lightbox&amp;#34;&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;figure&lt;/span&gt;&amp;gt;
      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;img&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;$thumb&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.RelPermalink&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;alt&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;$figcaption&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; 
        &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;$figcaption&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tn&amp;#34;&lt;/span&gt; /&amp;gt;
    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;figure&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lightbox&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.context.RelPermalink&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;md5&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-lightbox&amp;#34;&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#_&amp;#34;&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;img&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.context.RelPermalink&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;safeURL&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; /&amp;gt;
  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lightbox_overlay&amp;#34;&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;$figcaption&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;time&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dt-published&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;datetime&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.postDate&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.postDate&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;time&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.mainPermalink&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;}}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&amp;gt;View post&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#_&amp;#34;&lt;/span&gt;&amp;gt;Close&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the relevant CSS (I don&amp;rsquo;t use SASS):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;lightbox&lt;/span&gt; {
	&lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;none&lt;/span&gt;;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;position&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;fixed&lt;/span&gt;;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;top&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;left&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;right&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;bottom&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;z-index&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;999&lt;/span&gt;;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;vw&lt;/span&gt;;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;vh&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;background&lt;/span&gt;: rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0.8&lt;/span&gt;);
}
.&lt;span style=&#34;color:#a6e22e&#34;&gt;lightbox_overlay&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;position&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;fixed&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;z-index&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;bottom&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;background&lt;/span&gt;: rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0.6&lt;/span&gt;);
    &lt;span style=&#34;color:#66d9ef&#34;&gt;padding-bottom&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;rem&lt;/span&gt;;
}
.&lt;span style=&#34;color:#a6e22e&#34;&gt;lightbox_overlay&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;rem&lt;/span&gt;;
}
.&lt;span style=&#34;color:#a6e22e&#34;&gt;lightbox&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;img&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;position&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;absolute&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;top&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;left&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;right&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;bottom&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;margin&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;max-width&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;max-height&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;;
}
.&lt;span style=&#34;color:#a6e22e&#34;&gt;lightbox&lt;/span&gt;:&lt;span style=&#34;color:#a6e22e&#34;&gt;target&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;outline&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;none&lt;/span&gt;;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;block&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My only changes on top of the jlelse&amp;rsquo;s blog post linked above was to add a second overlay for a caption and date to be shown, and a link to the original post, and a close button. Clicking the post itself also dismisses the overlay (by replacing the URL fragment), but I thought it was more usable to also add an obvious Close button. I also initially wanted to put &amp;ldquo;Previous&amp;rdquo; and &amp;ldquo;Next&amp;rdquo; buttons so that you can easily cycle through the images in a post, but this was a bit more effort. I didn&amp;rsquo;t have a way to &amp;ldquo;look ahead&amp;rdquo; in the loop to get the &amp;ldquo;next&amp;rdquo; image, if any, so I think I would have needed to loop through the images twice? And since I use this image list in a number of places on the site, that was a bit troublesome, so maybe it&amp;rsquo;s something I&amp;rsquo;ll attempt later on?&lt;/p&gt;
&lt;p&gt;In any case, I think the new lightboxes are neat!&lt;/p&gt;



&lt;img src=&#34;https://mirror.roytang.net/2020/07/image-lightboxes-using-html/css/lightbox_hu396d03ff45b11c54616c22d72e1875ca_532641_300x0_resize_box_2.png&#34; /&gt;



&lt;img src=&#34;https://mirror.roytang.net/2020/07/image-lightboxes-using-html/css/photos_hua697e57b9c5f61f8be116df80d63f53b_922940_300x0_resize_box_2.png&#34; /&gt;



            </description>
        </item>
    
        <item>
            <title>Scraping Facebook
</title>
            <link>https://mirror.roytang.net/2020/06/scraping-facebook/</link>
            <pubDate>Wed, 24 Jun 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/06/scraping-facebook/</guid>
            <description>
            
            &lt;p&gt;I had been meaning to &lt;a href=&#34;https://mirror.roytang.net/2020/06/quitting-facebook/&#34;&gt;quit Facebook&lt;/a&gt; for more than a year maybe, but I kept putting it off. The main reason being that I like having backups of my own digital data (still very much a &lt;a href=&#34;https://mirror.roytang.net/2007/12/pack-rat-mentality/&#34;&gt;pack rat&lt;/a&gt;), and Facebook&amp;rsquo;s &lt;a href=&#34;https://mirror.roytang.net/2019/03/export-your-social-media-data/&#34;&gt;social media export&lt;/a&gt; is less than ideal, for me at least.&lt;/p&gt;
&lt;p&gt;Less than ideal why? It doesn&amp;rsquo;t include a lot of content I would like backed up, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;comments on my posts (there have been some good conversations with friends over the years I would prefer to preserve)&lt;/li&gt;
&lt;li&gt;things I&amp;rsquo;ve reposted from other people&lt;/li&gt;
&lt;li&gt;content of certain groups I&amp;rsquo;m members of (again, mostly for some interesting discussions over the years)&lt;/li&gt;
&lt;li&gt;pictures posted by other people that I&amp;rsquo;ve been tagged in&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a while I looked around for scraper programs/scripts to this for me, but none really did what I wanted. Early this month I finally wrote my own scraper scripts to pull that data out of Facebook, as a supplement to the actual FB export. It seems unlikely that these will be helpful to anyone else, especially since web scraping is an inherently brittle activity; any slight change in the FB HTML generation will break the scripts. In spite of that, I thought I&amp;rsquo;d share them publicly anyway, via Github gists.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gist.github.com/roytang/a5b4a8e4d511e97b9d25cb190f9dfb5d&#34;&gt;Script to export FB timeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gist.github.com/roytang/1cb3a682a2c223e569e3009aceb0e319&#34;&gt;Script to export FB group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are basically the same file, with minor changes between configuration and some scraping details. I wasn&amp;rsquo;t expecting to need these scripts again in the future so I didn&amp;rsquo;t bother with any kind of reusability.&lt;/p&gt;
&lt;p&gt;Some other notes about these scripts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;made with Python! Using my favorite HTML parsing library, BeautifulSoup&lt;/li&gt;
&lt;li&gt;the scripts run against the mobile web version m.facebook.com. The main web version is troublesome because it executes JS to do its infinite scroll and I didn&amp;rsquo;t want to waste time reverse engineering that&lt;/li&gt;
&lt;li&gt;there are some variables near the start that you should probably set. Most of it is output folders and group details, but the main one is the FB login cookie - basically you need to login to FB, then use dev tools or whatever to copy your cookie string&lt;/li&gt;
&lt;li&gt;the main output file is a gigantic JSON. There is no specified schema, basically I made things up as I went along. I&amp;rsquo;ll figure out how to process this at a later date lol.&lt;/li&gt;
&lt;li&gt;As a secondary output, there is also a cache folder that should contain the raw HTML parsed for each page. These aren&amp;rsquo;t necessary to retain, as all the information they have should also be in the JSON. Images from posts are also downloaded into a &lt;code&gt;dls&lt;/code&gt; folder. (The JSON will include the download path for the images from each post.)&lt;/li&gt;
&lt;li&gt;running the timeline export on my machine took the better part of 2-3 hours, for roughly 13 years worth of FB content. The output JSON was around 22 MB.&lt;/li&gt;
&lt;li&gt;running this might be considered spammy behavior by Facebook (understandably, but it&amp;rsquo;s their fault they won&amp;rsquo;t me do this via API), but the only consequence I&amp;rsquo;ve seen was that I wasn&amp;rsquo;t able to view other people&amp;rsquo;s profiles for some minutes after running the script&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As for what I&amp;rsquo;m going to do with this data, that&amp;rsquo;s still to be determined. The nonpublic content can be imported into the archive on this blog (if it isn&amp;rsquo;t there yet), but that&amp;rsquo;s a lot of manual work. As for the scripts, hopefully I won&amp;rsquo;t have to use them again in the future, but maybe they can be useful to someone else!&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Technical Interview Notes
</title>
            <link>https://mirror.roytang.net/2020/04/technical-interview/</link>
            <pubDate>Tue, 28 Apr 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/04/technical-interview/</guid>
            <description>
            
            &lt;p&gt;I&amp;rsquo;ve had the good fortune to be on the interviewer side of technical interviews much more often than I&amp;rsquo;ve been the interviewee. I&amp;rsquo;ve been doing a few more of these over the past couple of years and made some notes, so I thought I&amp;rsquo;d talk about technical interviews for a bit. Caveat: these are largely based on my own experiences, in the local environment here in the PH.&lt;/p&gt;
&lt;h3 id=&#34;technical-exam--screening&#34;&gt;Technical Exam / Screening&lt;/h3&gt;
&lt;p&gt;Many companies will ask applicants to undertake a technical exam before letting them advance to further stages of the recruitment process. This kind of screening is most useful if your applicants are mostly fresh graduates or junior programmers. The primary purpose of such a screening is to make sure that the candidate is actually a programmer, and not just someone who graduated CS while letting all his groupmates handle the programming work. This is especially useful for companies that get a lot of applicants who are fresh graduates - it lets them filter the candidate pool without using up the valuable time of their senior technical staff. I talked about these kinds of technical screenings in &lt;a href=&#34;https://mirror.roytang.net/2016/06/the-programming-application-process/&#34;&gt;a previous post&lt;/a&gt; about my own experiences as an interviewee.&lt;/p&gt;
&lt;p&gt;As a sample statistic, I used to handle checking of technical exams for a company that had a few dozen applicants per week. The screening was a straightforward ten-question written exam, none of the questions required writing a lot of code. The questions were along the lines of &amp;ldquo;where is the problem in the loop?&amp;rdquo;, or &amp;ldquo;what is the output of this program?&amp;rdquo;, essentially confirming the applicant&amp;rsquo;s ability to understand how code is written. Even for this simple screening, roughly 8-9 out of 10 applicants would regularly fail the screening. (I am not sure if this speaks to the general low quality of graduates in my area instead of a general trend.) There is a reason &lt;a href=&#34;https://en.wikipedia.org/wiki/Fizz_buzz&#34;&gt;FizzBuzz&lt;/a&gt; has become known as screening tool.&lt;/p&gt;
&lt;p&gt;The screening can take different forms. It may be written, requiring you to come into their office, sit at a table and answer some number of programming-related questions on paper (as described in the paragraph above). This is the most common I have encountered, especially in larger companies here.&lt;/p&gt;
&lt;p&gt;Some may be done remotely/online, via services such as &lt;a href=&#34;https://www.codility.com/&#34;&gt;Codility&lt;/a&gt;. This may be the best option, as it allows the candidate to take the exam on their own time, in the comfort of their own environment. Such environments will also automatically check the results, which means the effort for the recruiting company is at a minimum and the candidate gets feedback quickly. The downside is that not all companies may be able to afford such services.&lt;/p&gt;
&lt;p&gt;The worst option is when they do a phone screening - someone asking you technical questions over the phone. Phone screening is the worst because there are no real technical questions that can be of any practical use in evaluating a candidate over the phone. Most good technical screening questions will require a lot of text (better on paper than verbally over the phone) or may otherwise have nuance in the possible answers. For the latter case, it&amp;rsquo;s probably fine if they have a senior developer doing the phone screenings, but few companies can afford that, so most likely it will be some HR person who was provided with a fixed set of answers where if you don&amp;rsquo;t have the exact answer they have on their cheat sheet, you won&amp;rsquo;t pass the screening. As much as possible I would suggest to avoid any companies that use phone screening, as it is an organizational smell.&lt;/p&gt;
&lt;h3 id=&#34;technical-interview&#34;&gt;Technical Interview&lt;/h3&gt;
&lt;p&gt;The technical interview is different from a technical screening in that it usually involves face-to-face time with one or more interviewer(s). You will be usually given one or more technical problems, usually of a higher difficulty than would be present in a technical exam/screening. Usually, you will be asked to provide a solution on paper or on a whiteboard, and would be expected to walk the interviewer(s) through your solution. The purpose of this stage is for the interviewer to see personally how you think through the problem and how you devise a solution and (to a lesser extent) how well you can present it.&lt;/p&gt;
&lt;p&gt;I used to do a lot of these styles of interviews back in the day. At my old company, we had a pool of challenging technical interview questions, many of them requiring some level of algorithm knowledge in order to provide efficient answers. Many would require things like graph traversal, binary trees, that kind of thing. Some level of optimization is expected.&lt;/p&gt;
&lt;p&gt;There are several problems with this approach, the main one being it often doesn&amp;rsquo;t really test for how well a person would perform as a software engineer. For one thing, such an environment is not reflective of actual programmer work environments. If you&amp;rsquo;re interviewing for a web developer position, such complicated problems would come up in your day-to-day work maybe 5-10% of the time at best. (The numbers go up the lower level you go, such questions would be a lot more useful for systems programming work for example.) And unless you are hiring someone specifically for a high pressure position, this type of exam is biased against people with anxiety or who otherwise have trouble working with what is akin to a gun to their head. It is also effort-intensive on the part of the interviewer(s), since often you will need to sit through the candidate explaining their solution and maybe pointing out flaws or inefficiencies in the approach. So I&amp;rsquo;ve kind of soured on this approach and haven&amp;rsquo;t done it in a while, but there are specific situations where it would be useful.&lt;/p&gt;
&lt;p&gt;These days, what I like to do in technical interviews varies depending on the level of the role I&amp;rsquo;m interviewing for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;for fresh graduates or junior developer roles, I will generally ask 2-3 comparatively simple programming questions. Something only a bit more difficult than FizzBuzz, maybe along the lines of a fibonacci function or some array processing or such. This is especially useful if I&amp;rsquo;m doing this for a small company that doesn&amp;rsquo;t have the technical screening step, or to verify the results of the technical screening step. Afterwards, I will tend to do a short Q&amp;amp;A, asking some questions about projects they have worked on (maybe at school), what difficulties they have encountered, how they approach a problem etc.&lt;/li&gt;
&lt;li&gt;for more senior positions, I tend to skip programming questions altogether (I assume that they haven&amp;rsquo;t spent 5 or so years BSing their way through programming tasks) and prefer a more detailed Q&amp;amp;A, usually tailored towards the expectations for the role and their specific background. Typically I will ask questions that will require the candidate to expound on technical issues such as:
&lt;ul&gt;
&lt;li&gt;what was the biggest technical challenge you had to overcome?&lt;/li&gt;
&lt;li&gt;what are the advantages of X over Y and vice versa? (For example, for senior frontend devs, you can ask for the differences between React/Angular/Vue, if they have that experience). If you require specific experience in a particular technology, the interviewer will need to have sufficient experience as well, enough to be able to ask deeper questions while being able to filter out BS&lt;/li&gt;
&lt;li&gt;for this [past project], why did you choose this tech stack instead of [more common stack]? What are the pros and cons?&lt;/li&gt;
&lt;li&gt;can you describe a time when you had to convince upper management to use a risky technical approach?&lt;/li&gt;
&lt;li&gt;what was your worst technical mistake/fuck-up? (Follow-up: what did you learn, how did you recover, etc)&lt;/li&gt;
&lt;li&gt;have you ever had a problematic junior developer working with you? How did you handle it? (Such types of questions are good for technical lead roles)&lt;/li&gt;
&lt;li&gt;have you ever had to dive into a project with an unfamiliar tech stack without time for learning? How did you cope? What were the challenges? etc.&lt;/li&gt;
&lt;li&gt;what is your preferred approach for estimation? what if you are under schedule pressure?&lt;/li&gt;
&lt;li&gt;how do you prefer to onboard a new developer to a large project?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;My goal for such questions would be to look for absence of several attributes I consider important for senior developers:
&lt;ul&gt;
&lt;li&gt;able to communicate technical issues well (useful both when managing upwards and downwards)&lt;/li&gt;
&lt;li&gt;able to adjust to unfamiliar situations/technologies easily&lt;/li&gt;
&lt;li&gt;able to admit to mistakes on his part&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;effectiveness-of-technical-interviews&#34;&gt;Effectiveness of Technical Interviews&lt;/h3&gt;
&lt;p&gt;Ultimately, just doing the technical questions or the Q&amp;amp;A can&amp;rsquo;t guarantee the candidate will be good for the role. Evaluating the performance of individual developers is a notoriously hard problem, even when doing annual performance reviews, what more when limited to a one hour interview? For me, the purpose of a technical interview is to act as a negative screening tool - to filter out candidates who are obviously unfit for the position: candidates who can&amp;rsquo;t program, who can&amp;rsquo;t communicate well, who don&amp;rsquo;t have experience in the specific role or tools you need, and so on. The actual evaluation of effectiveness and fit should happen during a short probationary work period with the company.&lt;/p&gt;
&lt;h3 id=&#34;how-to-prepare-for-technical-interviews&#34;&gt;How to Prepare for Technical Interviews?&lt;/h3&gt;
&lt;p&gt;For candidates, how do you prepare for technical interviews?&lt;/p&gt;
&lt;p&gt;The most important question is how confident are you in your own programming skills? If you&amp;rsquo;ve done any nontrivial amount of programming work, generally you won&amp;rsquo;t have an issue. If you&amp;rsquo;re not confident, better to practice until you are, start with simpler questions and work your way up.&lt;/p&gt;
&lt;p&gt;For the Q&amp;amp;A part, in general you just need to be honest. But you also need to be able to communicate well, which is something not many people will be able to do. Good communication skill is an essential skill as a developer though, you may want to look for ways to practice and gain confidence in it. I recommend posting on a blog (like this one) to sharpen your communication skills! (Verbal is harder to practice, you need to find someone willing to listen and give you feedback.)&lt;/p&gt;
&lt;p&gt;I would also recommend becoming familiar with the requirements for the position you are interviewing for, understanding which parts of your background are most relevant, and highlighting them during any Q&amp;amp;A. In fact, I would prepare a short pitch describing why you think you are a good fit for the role. Often during Q&amp;amp;A I like to start off with asking the candidate to give a short overview of their background, and very few actually give a good response (a lot don&amp;rsquo;t even have a short response), so being prepared in this manner would be very impressive.&lt;/p&gt;
&lt;p&gt;It would also help to ask around about the specific recruitment process for the company you&amp;rsquo;re interviewing for, if you have any contacts, or sometimes there&amp;rsquo;s information on the internet. (Good internet searching is an essential developer skill as well!) Just knowing what to expect can boost your confidence.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also given some other advice in a &lt;a href=&#34;https://mirror.roytang.net/2019/02/reddit-ph-software-dev-qa/&#34;&gt;Q&amp;amp;A last year on reddit&lt;/a&gt;. Good luck!&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Everything I Know About Software Development
</title>
            <link>https://mirror.roytang.net/topics/dev/</link>
            <pubDate>Sun, 26 Apr 2020 13:27:12 +0800</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/topics/dev/</guid>
            <description>
            
            &lt;ul&gt;
&lt;li&gt;War stories
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/05/origin-story/&#34;&gt;How I got started programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/02/old-web/&#34;&gt;Old-school web development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/10/working-hours-and-overtime/&#34;&gt;Working hours and overtime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/02/working-with-client-server-programs/&#34;&gt;Working with Client-Server Programs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Getting a job as a software developer / hiring software developers
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/03/what-are-the-pros-and-cons-of-a-programming-career/&#34;&gt;Pros and cons of a career as a software developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/06/the-programming-application-process/&#34;&gt;The process of applying to be a software developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/01/job-hunting-for-programmers/&#34;&gt;Job Hunting for Programmers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/04/technical-interview/&#34;&gt;Technical Interview Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/02/reddit-ph-software-dev-qa/&#34;&gt;Software Dev Q&amp;amp;A on Reddit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/03/fake-devs-and-whiteboard-interviews/&#34;&gt;Fake devs and whiteboard interviews&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/07/10x-programmers/&#34;&gt;10X Programmers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/01/qualities-to-look-for-in-a-software-developer/&#34;&gt;Qualities to Look for in a Software Developer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Self-improvement
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/04/learning-from-failure/&#34;&gt;Learning from failure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/03/studying-a-large-project-codebase/&#34;&gt;Studying a large project codebase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/01/finding-time-to-learn-new-things/&#34;&gt;Finding Time to Learn new Things&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/01/i-still-google-the-most-basic-things/&#34;&gt;I still Google the most basic things&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/11/168-hours-vs-10000-hours/&#34;&gt;168 hours vs 10000 hours&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/12/learning-a-new-programming-language/&#34;&gt;Learning a New Programming Language&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Working with other people
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/06/working-with-testers/&#34;&gt;&amp;hellip; with testers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/04/working-with-people-from-other-cultures/&#34;&gt;&amp;hellip; with people from other cultures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/10/mentoring-in-software-development/&#34;&gt;Mentoring in Software Development&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Actually coding
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/12/my-coding-approach/&#34;&gt;My Coding Approach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/01/the-simplest-code-that-can-do-the-job/&#34;&gt;The Simplest Code That Can Do The Job&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/12/cleaning-up-your-code/&#34;&gt;Cleaning up your Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Software career perils
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/02/burnout/&#34;&gt;Burnout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/04/learning-from-failure/&#34;&gt;Learning from failure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/03/dont-stay-in-the-same-place-too-long/&#34;&gt;Don&#39;t stay in the same place too long&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Web Development
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/03/browsers-and-http/&#34;&gt;Browsers and HTTP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/04/web-application-security/&#34;&gt;Web Application Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/06/web-frameworks-open-source-or-roll-your-own/&#34;&gt;Web Frameworks &amp;ndash; Open Source or Roll Your Own?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/02/handling-unexpected-errors-in-web-applications/&#34;&gt;Handling unexpected errors in web applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/01/client-and-server-validation-in-web-applications/&#34;&gt;Client and Server Validation in Web applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Project-level technical concerns
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/11/version-issues/&#34;&gt;Version Issues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/02/integrating-open-source-libraries/&#34;&gt;Integrating Open Source Libraries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Project management
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/04/bespoke-vs-product-development/&#34;&gt;Bespoke vs Product Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/11/prod-crisis/&#34;&gt;What to do in a production crisis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/11/handover-documentation/&#34;&gt;The Perils of Handover Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/12/adding-developers-to-a-late-project/&#34;&gt;Adding Developers to a Late Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/10/change-and-risk-and-governments/&#34;&gt;Change and Risk and Governments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/10/gladwell-and-risk-management/&#34;&gt;Gladwell and Risk Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/01/problems-in-large-software-dev-teams/&#34;&gt;Problems in Large Software Dev Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/12/generalists-and-specialists-in-dev-teams/&#34;&gt;Generalists and Specialists in Dev Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/12/power-distance-in-software-development/&#34;&gt;Power Distance in Software Development&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Things I dislike
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/03/lies-and-marketing/&#34;&gt;Lies and marketing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tools
&lt;ul&gt;
&lt;li&gt;Source control
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/01/git-vs-cvs/&#34;&gt;Git vs CVS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/08/devnotes-migrating-mercurial-to-git/&#34;&gt;Devnotes: Migrating Mercurial to Git&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Editors
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/11/my-history-in-text-editors/&#34;&gt;My history in text editors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Internet
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/04/stackoverflow/&#34;&gt;StackOverflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Devnotes
&lt;ul&gt;
&lt;li&gt;HTML
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/04/pure-html-toggles/&#34;&gt;Pure HTML Toggles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CSS
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/03/pure-css-spoilers/&#34;&gt;Pure CSS Spoilers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Python
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/01/flask-vs-django/&#34;&gt;Flask vs Django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/11/python-yield/&#34;&gt;DevNotes: Python&#39;s yield&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/08/devnotes-python-pathlib/&#34;&gt;Devnotes: Python Pathlib&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/03/running-python-2-x-and-3-x-on-windows/&#34;&gt;Running Python 2.x and 3.x on Windows&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SQL
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/11/sql-dying-art/&#34;&gt;Is SQL a dying art?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/08/devnotes-postgresql-on-the-command-line/&#34;&gt;Devnotes: PostgreSQL on the command line&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;React Native
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/07/upgrading-a-react-native-project/&#34;&gt;Upgrading a React Native Project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;C++
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/01/revisiting-c-after-a-decade/&#34;&gt;Revisiting C++ after a decade&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JavaScript
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/12/javascript-references-to-out-of-scope-variables/&#34;&gt;Javascript: References to out-of-scope variables.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Others
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/04/marklogic-nosql/&#34;&gt;MarkLogic NoSQL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Mini-Projects
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2020/06/scraping-facebook/&#34;&gt;Scraping Facebook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/08/python-markov-chains/&#34;&gt;Python: Markov Chains&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/08/devnotes-tt-miniproject/&#34;&gt;Devnotes: TT Miniproject (Django Rest Framework, Unit Testing, VueJS, Geocoding, Nightwatch e2e Testing)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/03/a-quick-twitter-app-i-wrote/&#34;&gt;A Quick Twitter App I Wrote&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/02/hacktoberfest/&#34;&gt;Hacktoberfest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/02/triviastorm-text-and-answer-parsing/&#34;&gt;TriviaStorm: Text and Answer parsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/02/weekend-project-twitter-trivia-bot/&#34;&gt;Weekend Project: Twitter Trivia Bot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/10/django-blog-application/&#34;&gt;Django Blog Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/07/weekend-project-stack-bragger/&#34;&gt;Weekend Project: Stack Bragger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/10/weekend-project-1-mtg-utils-decklist-sharing-tool/&#34;&gt;Weekend Project #1: MTG Utils Decklist Sharing Tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2004/10/sinfest/&#34;&gt;Sinfest!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Misc:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/01/tech-to-learn-in-2019/&#34;&gt;Tech to Learn in 2019&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/01/solution-architect/&#34;&gt;Solution Architect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/06/software-development-and-government/&#34;&gt;Software Development and Government&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/03/google-code-jam-and-competitive-programming/&#34;&gt;Google Code Jam and Competitive Programming&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Things I&amp;rsquo;ve posted to Stack Overflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2018/02/48557175/&#34;&gt;Django syndication framework: prevent appending SITE_ID to the links&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/12/48011448/&#34;&gt;MarkLogic node.js: stream end event doesn&#39;t fire for certain collections&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/12/47880549/&#34;&gt;Marklogic Template Driven Extraction: How to define nullable in Javascript template?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/11/47513579/&#34;&gt;MarkLogic template driven extraction and triples: forming triples between array nodes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/11/47449002/&#34;&gt;MarkLogic template driven extraction and triples: dealing with array nodes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/11/47336545/&#34;&gt;Marklogic Optic API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/11/47333503/&#34;&gt;Marklogic Roxy: Calling a javascript module from app_specific.rb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/11/47216649/&#34;&gt;vis.js network - is there a setting to make the node labels stay the same size on zoom?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/11/47129470/&#34;&gt;Marklogic Roxy and Template Driven Extraction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/10/47018820/&#34;&gt;Marklogic 9 + Roxy: can&#39;t connect to created database using Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/10/47009063/&#34;&gt;Marklogic 9: MLCP fails when importing records with a transform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/10/46729939/&#34;&gt;React Native: How to use Webview?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/10/46579945/&#34;&gt;vis.js - shorter edge lengths for higher edge value?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/09/46264021/&#34;&gt;Export HTML content with Chart.jscanvases to PDF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/09/46263980/&#34;&gt;MarkLogic - query for documents where a specific json property is not defined&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/09/46112486/&#34;&gt;MarkLogic node.js - conditional updates using fields other than versionId&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/08/45901868/&#34;&gt;MarkLogic node.js: sorting by the value of a json element with multiple instances in the same document&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/08/45623599/&#34;&gt;Marklogic - flexible replication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/08/45507423/&#34;&gt;MarkLogic node.js - is it possible to support a derived value in parseBindings?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/08/45463003/&#34;&gt;Marklogic: Delete all element range indexes on a database?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/07/45113954/&#34;&gt;Moving connector edges in jsplumb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/07/45113755/&#34;&gt;jsplumb - scroll the canvas when node dragged outside visible area&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/07/45093664/&#34;&gt;MarkLogic - detecting similar/duplicate names&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/06/44804960/&#34;&gt;Submit a URL as a file in a form&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2017/06/44621849/&#34;&gt;MarkLogic node.js api - group by sum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/12/40968519/&#34;&gt;Marklogic Node.js API: How to get the document where an embedded triple lives?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/12/40968002/&#34;&gt;How to query Marklogic triples using SPARQL?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/11/40720684/&#34;&gt;Marklogic Node API - how to filter the results from a valuesBuilder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/11/40715822/&#34;&gt;MarkLogic node.js api - group by and sort by count&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2015/09/32558234/&#34;&gt;Invalid SSL certificate in Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2014/06/24115461/&#34;&gt;Java windows app - is it possible to detect if the current device has touchscreen capability?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2014/06/23992330/&#34;&gt;How to convert an offset from GMT to the local timezone?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2014/05/23645265/&#34;&gt;Adding multitouch support to an AWT application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2014/05/23622915/&#34;&gt;Developing Java desktop applications on Microsoft Surface Pro/Pro 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2014/02/21730829/&#34;&gt;How can I find out which slow script is giving IE problems?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2013/11/19805338/&#34;&gt;Adding event handlers globally to all CKEditor instances&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2013/10/19397197/&#34;&gt;CKEditor - Can I trigger inline editing programmatically?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2013/07/17855992/&#34;&gt;Differences between IE 8 and IE 8 Compatibility View Browser Modes?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2013/04/16184236/&#34;&gt;CKEditor 4 - how to add font family and font size controls to the toolbar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2013/04/16011483/&#34;&gt;CKEditor inline editing - adding hint text&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2013/04/16009411/&#34;&gt;CKEditor inline editing - content isnt editable?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2013/03/15611653/&#34;&gt;Implementing HTTP Basic Authentication in a servlet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2013/03/15474727/&#34;&gt;jquery: listing out ready handlers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2012/08/12170462/&#34;&gt;How to assess the risk of a java version upgrade?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2012/07/11535910/&#34;&gt;Real-time database synchronization across Amazon Oracle RDS instances&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2012/07/11305956/&#34;&gt;Capturing and replacing the JSP response in a Filter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2012/01/8738408/&#34;&gt;Search filter using a bean&#39;s field values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2012/01/8720536/&#34;&gt;Comparison of Java web framework usage, especially in enterprise projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2011/12/8599475/&#34;&gt;Java API for OpenText eDocs/Hummingbird&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2011/12/8383151/&#34;&gt;Does compliance to WCAG 2.0 AA prevent the use of JavaScript?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2011/10/7871245/&#34;&gt;CSS Menu wrapping&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2011/07/6677574/&#34;&gt;Migrating from CVS to distributed version control (Mercurial)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2011/07/6593628/&#34;&gt;Oracle BPM and Oracle ADF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2011/03/5306112/&#34;&gt;Local VCS for one person use&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2011/01/4602149/&#34;&gt;Opening PowerBuilder 6.5 source files in PowerBuilder 11&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/12/4468271/&#34;&gt;Lucene sorting chinese characters by strokes/radicals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/10/3951840/&#34;&gt;How to invoke a function on an object dynamically by name?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/10/3931119/&#34;&gt;Configuring container-managed security in Weblogic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/09/3727551/&#34;&gt;Django - is there a way to view the queries being executed by the ORM?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/07/3356868/&#34;&gt;What are the methods of protecting JavaScript web applications/games?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/07/3308615/&#34;&gt;Proper way to contain floating elements using HTML/CSS?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/06/3051295/&#34;&gt;jquery-like HTML parsing in Python?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/01/2084133/&#34;&gt;Oracle Thin Driver and Transparent Failover of RAC DB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/01/2062066/&#34;&gt;Far future expire header and HTTP 304&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/01/2053919/&#34;&gt;screen sharing library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2010/01/2004096/&#34;&gt;Alternatives to Bouncy Castle java library for PKCS7 encryption/signing?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/12/1951373/&#34;&gt;Automated regression tests for java applets?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/12/1927273/&#34;&gt;Applet freezes page on initial load (Mac Firefox)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/12/1899374/&#34;&gt;How to stop further request execution from inside a taglib?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/12/1892706/&#34;&gt;Word doc document.PrintOut won&#39;t print to a particular printer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/12/1845277/&#34;&gt;Generating java core dump log file for applets in Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/12/1830908/&#34;&gt;CSS Rendering List Items in 2 rows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/11/1801804/&#34;&gt;Applets failing to load&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/11/1754697/&#34;&gt;Displaying Chinese text in an Applet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/11/1754135/&#34;&gt;Applet with JDialog not hiding correctly in Mac OSX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/11/1740843/&#34;&gt;How to enforce a site-wide license?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/10/1550532/&#34;&gt;Trimming whitespace from HTML content?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/10/1547415/&#34;&gt;how to handle IE select onchange and ajax requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/09/1491562/&#34;&gt;Loading and resizing images dynamically&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/09/1366638/&#34;&gt;Embedding videos in web pages/Firefox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/08/1276714/&#34;&gt;Invoking Java code across Application Server cluster nodes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/08/1225798/&#34;&gt;Javascript - programmatically invoking events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/07/1123415/&#34;&gt;styling a disabled combobox in internet explorer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/07/1090202/&#34;&gt;Using Javascript to open a &amp;quot;maximized&amp;quot; window in IE6?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/06/1047137/&#34;&gt;How to specify the word-breaking conditions in IE?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/06/1000028/&#34;&gt;How to focus radio control using Javascript in IE?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/04/765379/&#34;&gt;wxAuiNotebook - preventing certain tabs from closing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/01/441755/&#34;&gt;Regular expression to remove hostname and port from URL?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/12/376725/&#34;&gt;Firefox does not render stylesheet immediately?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/12/349997/&#34;&gt;What does this Regular Expression do&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/228386/&#34;&gt;How to get command line arguments for a running process&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/228382/&#34;&gt;C# HTML Font Tag Parsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/217801/&#34;&gt;Getting Language for Non-Unicode Programs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/09/119927/&#34;&gt;Different framerate for loaded SWF file in Flex?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/09/100247/&#34;&gt;Reading a PNG image file in .Net 2.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/09/99271/&#34;&gt;C# COM Office Automation - RPC_E_SYS_CALL_FAILED&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unsorted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/11/your-product-should-be-easy-to-install/&#34;&gt;Your Product Should Be Easy to Install&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/11/unclear-error-messages/&#34;&gt;Unclear error messages&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/11/be-willing-to-throw-prototypes-away/&#34;&gt;Be Willing To Throw Prototypes Away&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/11/software-development-feedback-loops/&#34;&gt;Software Development Feedback Loops&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/10/it-should-be-easy-right/&#34;&gt;It Should Be Easy, Right?&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/10/favor-composition-over-inheritance/&#34;&gt;Favor Composition Over Inheritance&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/10/coding-frameworks/&#34;&gt;Coding Frameworks&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/10/a-programmers-hubris/&#34;&gt;A Programmer&#39;s Hubris&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/09/type-interfaces-matter/&#34;&gt;Type Interfaces Matter&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/09/overhead-scales-up-rapidly-in-software-projects/&#34;&gt;Overhead scales up rapidly in software projects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/09/overtime/&#34;&gt;Overtime&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/09/will-ai-replace-all-our-jobs/&#34;&gt;Will AI replace all our jobs?&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/09/the-no-1-problem-in-software-projects/&#34;&gt;The No. 1 Problem in Software Projects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/08/the-secrets-to-perfect-estimates/&#34;&gt;The Secrets to Perfect Estimates&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/08/programming-is-finding-the-right-arrangement-of-code/&#34;&gt;Programming is Finding the Right Arrangement of Code&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/08/living-with-development-standards/&#34;&gt;Living With Development Standards&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/08/pros-and-cons-of-a-career-in-programming/&#34;&gt;Pros and Cons of a Career in Programming&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/07/evaluation-of-programmer-performance/&#34;&gt;Evaluation of Programmer Performance&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/07/learning-to-sql/&#34;&gt;Learning to SQL&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/07/certifications/&#34;&gt;Certifications&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/07/web-security-file-upload-vulnerabilities/&#34;&gt;Web Security: File Upload Vulnerabilities&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/06/performance-optimization-for-wordpress-blogs/&#34;&gt;Performance Optimization for WordPress Blogs&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/06/working-with-testers/&#34;&gt;Working with Testers&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/06/password-security-for-application-developers/&#34;&gt;Password Security for Application Developers&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/06/the-programming-application-process/&#34;&gt;The Programming Application Process&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/06/you-are-not-your-code/&#34;&gt;You Are Not Your Code&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2016/05/origin-story/&#34;&gt;Origin Story&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2015/10/10154142633048912/&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/12/new-comments-system/&#34;&gt;New Comments System&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/06/contest-details-extend-firefox-3-5/&#34;&gt;Contest Details | Extend Firefox 3.5&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/03/the-joel-on-software-discussion-group-best-practices-for-code-sharing-among-a/&#34;&gt;The Joel on Software Discussion Group - Best Practices for code sharing among a &amp;hellip;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/02/stylesheet-switcher-using-django/&#34;&gt;Stylesheet Switcher using Django&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/02/zed-shaw-the-acl-is-dead/&#34;&gt;Zed Shaw &amp;ndash; the ACL is Dead&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/02/code-quality/&#34;&gt;Code Quality&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/01/using-django-pingback/&#34;&gt;Using Django Pingback&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2009/01/com-surrogate-has-stopped-working-error-in-vista/&#34;&gt;COM Surrogate Has Stopped Working Error in Vista&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/12/syntax-highlighting-using-pygments/&#34;&gt;Syntax Highlighting using Pygments&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/12/colophon-2008/&#34;&gt;Colophon 2008&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/12/fixing-up-the-comments/&#34;&gt;Fixing up the comments&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/11/django-update-wordpress-import-and-more/&#34;&gt;Django Update &amp;ndash; WordPress Import and More&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/11/free-flex-shirt-from-adobe/&#34;&gt;Free Flex Shirt from Adobe&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/django-tagging/&#34;&gt;django-tagging&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/i-left-it-for-a-while/&#34;&gt;I left it for a while&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/deployment-problems/&#34;&gt;Deployment Problems&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/philippine-airlines/&#34;&gt;Philippine Airlines&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/playing-with-generic-views-and-urls/&#34;&gt;Playing with Generic Views and URLs&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/roy-on-django/&#34;&gt;Roy on Django&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/10/starting-out/&#34;&gt;Starting out&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/09/flashdevelop-open-source-flexactionscript-ide/&#34;&gt;FlashDevelop &amp;ndash; open source Flex/Actionscript IDE&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/09/sms-mms-api/&#34;&gt;SMS/MMS API&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/08/poorly-implemented-combo-box/&#34;&gt;Poorly Implemented Combo Box&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/08/django-and-google-app-engine/&#34;&gt;Django and Google App Engine&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/08/barcamp-manila/&#34;&gt;Barcamp Manila&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/08/the-survey-for-people-who-make-websites/&#34;&gt;The Survey for People Who Make Websites&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/08/new-facebook/&#34;&gt;New Facebook&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/08/google-code-jam-2008/&#34;&gt;Google Code Jam 2008&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/06/google-treasure-hunt/&#34;&gt;Google Treasure Hunt&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/05/creating-a-wordpress-template-part-3/&#34;&gt;Creating a WordPress Template Part 3&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/05/creating-a-wordpress-template-part-2/&#34;&gt;Creating A WordPress Template Part 2&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/04/creating-a-wordpress-template-part-1-of-n/&#34;&gt;Creating a WordPress Template (Part 1 of N)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/03/trust-and-evil-programmers/&#34;&gt;Trust and Evil Programmers&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/02/mtg-autocard-wordpress-plugin/&#34;&gt;MTG Autocard WordPress Plugin&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2008/01/job-hunting-for-programmers/&#34;&gt;Job Hunting for Programmers&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2007/10/how-to-solve-technical-problems/&#34;&gt;How to Solve Technical Problems&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2007/08/announcing-pymtg/&#34;&gt;Announcing PyMTG&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2007/07/rich-internet-applications/&#34;&gt;Rich Internet Applications&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2007/02/software-is-hard-salon-books/&#34;&gt;Software is hard | Salon Books&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/06/my-job-such-as-it-is/&#34;&gt;My Job, Such As It Is&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/06/epowiki-main/&#34;&gt;epowiki: Main&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/05/developer-assessment/&#34;&gt;Developer Assessment&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/05/google-web-toolkit/&#34;&gt;Google Web Toolkit&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/05/gmail-raises-the-bar-for-everyone/&#34;&gt;GMail raises the bar for everyone&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/04/why-specs-matter-dive-into-mark/&#34;&gt;Why specs matter [dive into mark]&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/02/meet-joe-bloggs/&#34;&gt;Meet Joe Bloggs&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/02/wikiwikiweb-front-page/&#34;&gt;WikiWikiWeb Front Page&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/01/users-don39t-know-what-they-want/&#34;&gt;Users don&#39;t know what they want.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2006/01/how-to-organize-a-cluttered-mind/&#34;&gt;How to Organize a Cluttered Mind?&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2005/12/random-java-programs/&#34;&gt;Random Java Programs&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2005/09/looking-for-a-phone-and-finding-bad-webapps/&#34;&gt;Looking for a phone and finding bad webapps&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2005/08/htmlcss-trickery/&#34;&gt;HTML/CSS Trickery&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2005/06/yahoo/&#34;&gt;Yahoo!&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2005/05/css-based-design/&#34;&gt;CSS-Based Design&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2005/03/in-progress-but-sleepy/&#34;&gt;In Progress, But Sleepy&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2005/03/developer-code-as-design-three-essays-by-jack-w-reeves/&#34;&gt;developer.* - Code as Design: Three Essays by Jack W. Reeves&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2005/01/how-to-be-a-programmer-a-short-comprehensive-and-personal-summary/&#34;&gt;How to be a Programmer: A Short, Comprehensive, and Personal Summary&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2004/12/teach-yourself-programming-in-ten-years/&#34;&gt;Teach Yourself Programming in Ten Years&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2004/12/the-graphing-calculator-story/&#34;&gt;The Graphing Calculator Story&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2004/12/bramcohen-great-programmers/&#34;&gt;bramcohen: Great Programmers&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2004/12/excel-problem/&#34;&gt;Excel problem&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2004/11/secretgeek/&#34;&gt;secretGeek&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2004/10/best-software-essays/&#34;&gt;Best Software Essays&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2004/10/sinfest/&#34;&gt;Sinfest!&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2004/10/this-is-me-being-surprised/&#34;&gt;This is me being surprised&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2003/12/that39s-l33t/&#34;&gt;That&#39;s L33T&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2003/10/the-first-time-i-hated-users/&#34;&gt;The First Time I Hated Users&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2003/01/23-jan-2003-working-joe/&#34;&gt;23 Jan 2003: Working Joe&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;



            </description>
        </item>
    
        <item>
            <title>Working with people from other cultures
</title>
            <link>https://mirror.roytang.net/2020/04/working-with-people-from-other-cultures/</link>
            <pubDate>Tue, 21 Apr 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/04/working-with-people-from-other-cultures/</guid>
            <description>
            
            &lt;p&gt;Unless you’re working at a small shop that only serves local clients, software development these days is often an international endeavor. That means the aspiring software developer needs to be able to work with and get along with people of different cultures.&lt;/p&gt;
&lt;p&gt;In the company I worked with, most projects back in the day we would have a person from the foreign office in charge. Their roles were either as project manager (PM) or system analyst (SA). They were the ones who would be interacting directly with the clients so they get to decide which things need to be done and will often also be setting the schedule. The PM/SA of my first project often communicated directly (via IM) only with his local counterpart, our team leader (TL), so at first our contact with him was minimal. (I found out later that he wasn’t too comfortable speaking to newbies as he’s afraid of being misinterpreted.) But as is wont in software development, often schedules will not be sufficient and deadlines will not be met and so there will be pressure to keep up from the PM/SA. When you’re starting out, there can be a tendency to be off put by these faceless individuals who communicate with us only email, often sending new problems our way.&lt;/p&gt;
&lt;p&gt;The PM/SA often had a vastly different culture from us Filipinos who like to joke around and be informal when discussing even work-related matters. And in our case, their first language was Chinese and not English. While they did learn English in school, they use it more formally instead of conversationally. So often their messages or emails will be terse, and when you’re not used to dealing with them you might even view them as bit rude or cold and uninviting.&lt;/p&gt;
&lt;p&gt;As I mentioned, the PM of our first project usually often spoke directly only with our TL, but eventually he warmed up to me and one of the newbie testers who was my batchmate. We often had to discuss or clarify program requirements with him so I guess he got used to us after a while. There was another newbie tester on our team however who he never seemed to warm up to. I remember him asking our TL “Who is &amp;lt;tester’s username&amp;gt;?” even though he had previously replied to some emails of the same tester. There was also one time I remember when that tester sent a detailed email to the PM to clarify some items, and the PM replied but addressed his reply to our TL instead! We would often joke around with that tester that the PM probably still had no idea who he was.&lt;/p&gt;
&lt;p&gt;Because they often speak in a formal manner, we are often surprised when a glint of humanity shines through. A remember when one other junior developer was talking on the phone with the PM about a particular problem in production and he came back all worried. When we asked him what happened he said “&lt;PM&gt; said some swear words!” Wow, we must have really been in deep trouble for him to lose his cool!&lt;/p&gt;
&lt;p&gt;It turns out not all PM/SAs were like that though. Later a couple of younger SAs joined our team and they were much more agreeable to talk to, willing to joke around a bit and talk about their day and stuff. I think it’s because they were fresh from University so maybe they were still used to English. It’s usually the older PMs who are much more stoic.&lt;/p&gt;
&lt;p&gt;Later on, I got sent to the foreign office to help with some UAT preparation on-site. The stoic PM met me at the office and took me to dinner and drove me around to some tourist spots on my first night, so we got a bit closer. The next day was a Saturday, and we had to go to the UAT site to check the setup and preparations. I met him at the office before going to the site and I was surprised to find that he was going to go to the UAT site in a T-shirt and shorts. After that, I never took him too seriously!&lt;/p&gt;
&lt;p&gt;A significant number of years later, I also got to work with some staff from a European country. It was another culture shift, but this time it was a bit easier. Unlike our Chinese friends, the Europeans were friendlier and more small-talkish. Meetings (even over Skype) would often start off with “Hello, how are you guys doing?” and more pleasant things, in those precious few minutes while waiting for everyone to get set up. It’s not all good though, as I distinctly remember a meeting where one of the callers was coughing and wheezing all the time (learn to use mute!), although that was probably a person-specific thing and not a cultural thing.&lt;/p&gt;
&lt;p&gt;Even work-wise, it was a vastly different way of working with the Europeans. With the Chinese people we worked with on our projects before, everything was very formal and well-documented and there would be action items and detailed program specs to follow and any changes had to go through a rigid process and schedules would need to be adjusted and such. With the Europeans, we would often have meetings and many of the items would not be resolved and would be moved to the next meeting. It would not be unusual for discussions to go round and round a few times before finally managing to come to a decision. And we’d never get clear specifications from them, only some general items or ideas of what they wanted, and we really had to iterate and show them how things would look before they would sign off (and even if they did sign off, they’re very likely to change their minds later at a moment’s notice!) If you’ve ever seen the BBC show W1A, it’s a pretty accurate portrayal of how it was!&lt;/p&gt;
&lt;p&gt;Working remotely and communication via channels like email, IM or video calls is already a challenge even when working with other locals. The additional dimension of a foreign culture means we have to be much more willing to adjust to other people as needed. This means being more open to how other people work with, giving them the benefit of the doubt, clarifying where things seems unclear or where there may be miscommunication, and so on.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Pure HTML Toggles
</title>
            <link>https://mirror.roytang.net/2020/04/pure-html-toggles/</link>
            <pubDate>Fri, 17 Apr 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/04/pure-html-toggles/</guid>
            <description>
            
            &lt;p&gt;Just last month, I wrote a method of implementing &lt;a href=&#34;https://mirror.roytang.net/2020/03/pure-css-spoilers/&#34;&gt;element toggles using a pure CSS approach&lt;/a&gt;. While that post was educational for me, it turns out there was an even simpler way of doing things. I found out about it when I read this &lt;a href=&#34;https://www.jvt.me/posts/2019/05/19/html-toggle/&#34;&gt;post by Jamie Tanna&lt;/a&gt;. Apparently the &lt;code&gt;details&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt; tags already support HTML toggles, so we can do this with neither CSS or JS!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve updated the spoiler tags on this site to use this new method. I also used this method for the Table of Contents on certain posts (currently only the &lt;a href=&#34;https://mirror.roytang.net/2020/03/covid19/&#34;&gt;Covid19 diary&lt;/a&gt;). Sample here:&lt;/p&gt;
&lt;details class=&#34;spoiler&#34;&gt;
    &lt;summary&gt;(Spoilers)&lt;/summary&gt;
    This is some text!
&lt;/details&gt;
&lt;p /&gt;
&lt;p&gt;The post I linked above covers most of the technical details. My only issue with this is that&amp;rsquo;s a relatively new feature. Even though browser support is near universal (minus the much-maligned and quite ancient Internet Explorer), I&amp;rsquo;m not sure if there will be issues or how it renders when viewed in an older browsers or in other contexts such as in a feed reader or a screen reader. I don&amp;rsquo;t want to accidentally show spoilers! I suppose that&amp;rsquo;s a minor concern at this point, and maybe I should just be confident that people who make those tools have taken the visibility toggle into account.&lt;/p&gt;
&lt;p&gt;(I&amp;rsquo;ve also edited the CSS post to point to this one so anyone who sees it has better information!)&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Web Application Security
</title>
            <link>https://mirror.roytang.net/2020/04/web-application-security/</link>
            <pubDate>Thu, 16 Apr 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/04/web-application-security/</guid>
            <description>
            
            &lt;p&gt;A while back one of the biggest leaks of personal information in history was made by hackers accessing the Commission on Elections database here in the Philippines. More than 50 million voter registration records, including information such as full names, date of birth, address, among others. A small percentage of the data leaked also included email addresses and even passport numbers. The hack exposed more than half of the country’s population to the possibility of social engineering and other exploits.
It was certainly a national embarrassment, especially for those of us working in software development. But the truth of the matter is that securing publicly accessible sites against determined bad actors is difficult, very difficult. It’s something that needs to be taken into account on all levels of the system.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t consider myself any kind of security specialist, but I think it&amp;rsquo;s something that software developers of all levels need to at least have a passing knowledge of, especially in this modern age where many tech companies store personal/private data for large numbers of users. I tend to classify the security practices and vulnerabilities under several levels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Physical / hardware layer - this means controlling the physical access of unauthorized personnel to hardware that contains private information. It would take serious levels of screw-ups for a system to be compromised in this manner, or perhaps a well-placed bribe. It is however, comparatively easy to detect any breaches in this layer. In any case, this is not really a concern for the software engineer.&lt;/li&gt;
&lt;li&gt;Server OS and local network level - this means that controlling the access to the actual OS running on the machine and the corresponding local network the server is on. Practically as bad (sometimes worse) than compromising the hardware layer, a compromise on this layer means the bad actor can access everything, possibly remotely, making it much more difficult to detect. Securing your system on this layer involves among other things making sure known vulnerabilities on your OS are patched or worked around and network access is restricted as needed. This is often the concern of people like network engineers or devops people who are setting up and securing your servers.&lt;/li&gt;
&lt;li&gt;Application container level - this doesn’t necessarily mean your application itself, but typically websites and web applications will be running inside a container software such as a webserver like Apache, Tomcat, ISS, etc. Securing this layer is often just a matter of making sure the latest security patches are applied and known vulnerabilities are closed and access is restricted as needed. If this layer is compromised, the bad actors will typically have access to any files deployed in the container (which may contain other information that will facilitate further leaks). I also include here securing vulnerabilities in the database software used as your backend.&lt;/li&gt;
&lt;li&gt;Application level - this refers to your security in the design of your application itself. Typically, problems in the first three layers above will be the responsibility of people setting up the servers and the server software, who are probably a different set of people than the development team. Concerns from this layer onwards are more applicable to the actual application developers.  Application-level security can be further broken down into several levels:
&lt;ul&gt;
&lt;li&gt;User authentication - this refers to your application’s implementation of user login and session control, and management of things like passwords, password reset requests, two-party authentication, etc. If the implementation is poor, your application may have vulnerabilities like bad actors being able to identify your users’ usernames by enumerating login requests or being able to brute force their passwords.&lt;/li&gt;
&lt;li&gt;Functional security - this refers to how your application grants rights to each user as to which functions in the system he is able to access. As an example, in a typical system, normal users should not be able to access functions restricted to administrator users.&lt;/li&gt;
&lt;li&gt;User-specific Data security - this ensures that only relevant data for each user can be accessed by each user. For example, if I’m logged in as User ID = 1, I shouldn’t be able to view the data for User ID = 2 simply by changing a parameter such as USER_ID=2 in the URL.&lt;/li&gt;
&lt;li&gt;Domain-specific security - this refers to additional security rules specific to the domain your application is involved in. For example, your security rules may demand that sensitive information can only be viewed by users with specific privileges and would need to be masked for other users. This may introduce some special logic in your program development.&lt;/li&gt;
&lt;li&gt;Logging - applications typically log a lot of information to log files and audit trails, to facilitate debugging of problems and often also for statutory purposes. Security restrictions may require making sure that sensitive data is not included in the logs and audit trails.&lt;/li&gt;
&lt;li&gt;Code level - this refers to security in coding practices in both the code used to write your application and also any third-party libraries or frameworks used by the application. This is highly experience-based and requires knowledge of the most common vulnerabilities as a result of coding practices. Organizations such as OWASP regularly publish lists of the most common vulnerabilities, many of them resulting from poor coding decisions such as SQL injection, cross-site scripting, cross-site request forgery, etc. Modern frameworks will typically provide standard ways for developers to minimize the chance of introducing the most well-known vulnerabilities.&lt;/li&gt;
&lt;li&gt;Database level - this refers to security in the design of the database itself. Examples of concerns on this level are whether sensitive information is encrypted or hashed or whether financial data such as credit card information is stored in the database and secured appropriately (for credit card information specifically, there are standards laid out for managing credit card data that you have to comply with in order to be certified to handle credit card transactions). I guess I would also include in this layer how database backups and purged data are handled - are they backed up and encrypted separated from the main database? - although the protection of such backups may be outside the scope of the application developer.&lt;/li&gt;
&lt;li&gt;Third party code - modern day applications use a lot of third party libraries and packages, not to mention code that developers are copying from StackOverflow. And rightly so - since using these dependencies means savings in effort and more productivity. But developers need to be aware of the cost every time we add a new dependency; not only are we dependent on that third party library working correctly, but also that it is generally secure. And if a security issue is found, will it be fixed in a timely manner? And this kind of thing can be difficult to track. There have been stories of websites being exposed to security breaches or even things like embedded crypto miners simply because they added a node.js dependency that had another dependency that had another dependency that was compromised. It&amp;rsquo;s the kind of thing more senior developers on the team should be aware of and on the lookout for.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In modern settings, developers often specialize into roles like frontend and backend. And one might think that security concerns are limited only to the backend side, but one should not neglect security on the front end either. There are security concerns on the browser/JavaScript level as well, and you have to make sure that your frontend is aligned with the backend&amp;rsquo;s security paradigms.&lt;/p&gt;
&lt;p&gt;Often times many clients will require that a third-party security audit will be done near the end of the project, and the development team will need to address any security concerns that come up during the audit. I’ve been on the receiving end of these a few times and I’ve found the companies doing them to be fairly thorough.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s better for your dev team to be generally aware of security best practices from the very beginning, as it may be more costly to correct security problems towards the end of a project. It&amp;rsquo;s the kind of valuable tribal knowledge among developers that you can carry easily across projects and companies and tech stacks.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Bespoke vs Product Development
</title>
            <link>https://mirror.roytang.net/2020/04/bespoke-vs-product-development/</link>
            <pubDate>Tue, 14 Apr 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/04/bespoke-vs-product-development/</guid>
            <description>
            
            &lt;p&gt;For most of my time working on software projects, it has always been for bespoke projects. Bespoke basically means a software program or package tailor-made for a specific client. The client provides all the requirements, the team fleshes out more details and specifications, some prototyping may or may not ensure, and implementation proceeds thusly. It’s relatively straightforward compared to product development.&lt;/p&gt;
&lt;p&gt;I only started getting involved with “product”-like projects over the in the latter half of my career as a software developer. A &amp;ldquo;product&amp;rdquo; is a more general use software program or package, perhaps sold to the mass market or as a service. My experience is mostly with products that are targeted towards the public sector (government agencies and such) and not for general consumer use. Though I imagine many similar issues and pitfalls as I discuss here can occur in other types of product development as well. These are largely issues of &amp;ldquo;building for multiple clients&amp;rdquo; as opposed to &amp;ldquo;building for a specific client&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The project lifecycle is markedly different: you start with an idea for a product, perhaps based on something you’ve already built before, and shop it around to potential clients who could use such a system. A number of clients put out tenders for projects where they specify it has to be a pre-built product instead of a bespoke solution. Typically you use the requirements from these tenders to direct your development while you don’t have clients signed up yet. That’s right, you start development work ahead of time even when you don’t have clients signed up yet. These tenders typically have significant lead time before the actual implementation, so you kind of hope that if you somehow magically win with your proposal, you have everything (or at least most of it) done and in place and ready for the client.&lt;/p&gt;
&lt;p&gt;Tenders can also sometimes have a requirement to have a demonstration during the tendering process. Therein lies the first problem - since you probably don’t have the product ready yet, there’s probably some crunch time needed in preparing and setting up the demo. Some iffy code will need to be written, sacrificing code quality and maintainability in order to meet the timescales for the demo.&lt;/p&gt;
&lt;p&gt;Of course, since you want to gain traction as quickly as possible, you try to shop around to as many clients as you can. That means you try to bid for as many tenders as you can, and different clients can have different requirements. This of course creates more problems for the development team in the form of conflicting requirements and even worse, possibly conflicting demo schedules. It’s something that management has to be cognizant of and the company will need to pick and choose its battles. Poorly handled, it could put a lot of pressure on the development team.&lt;/p&gt;
&lt;p&gt;I was discussing this the other day with a younger developer working in another company. They were working on a project that was headed in this direction and he was asking whether things would get better once they have more things, and I kind of laughed a little bit before telling him there’s a good chance it would get worse.&lt;/p&gt;
&lt;p&gt;The problem of conflicting requirements gets even more complicated when the clients are actually paying for your product because now you have even more responsibility to fulfill their custom requirements. One would think that if you were making an off the shelf product you’re not really subject to the whims of the clients too much, but when you’re trying to gain traction in an existing market you will sometimes need to make compromises to get clients to choose you over more established products, which probably means catering more to their whims.&lt;/p&gt;
&lt;p&gt;And really, even clients in the same space needing the same product will have their own requirements, idiosyncrasies and ways of doing things. Sometimes even when there are statutes in place dictating standards (like what data should be recorded and so on), different clients will still have different ways of meeting those standards such that your system may not be able to cater for all of the differences.&lt;/p&gt;
&lt;p&gt;Ideally, your product has been planned with such variations in requirements in mind. This means making your system flexible and/or configurable enough to handle any differences between clients, or at least changes can be kept small and manageable between clients. This assumes of course that you had the foresight to predict ahead of time what the differences would be and planned your system architecture accordingly, and that you didn’t have a lot of poor and hard-to-maintain code littered throughout your system because of needing to meet conflicting demo requirements! The more customizable your system needs to be, the more well-structured, modular and lightly coupled the code has to be.&lt;/p&gt;
&lt;p&gt;If you don’t have the above, well then likely you’re looking at introducing even more bad code and stretching your code base with code smells everywhere in order to accommodate tight deadlines and conflicting requirements.&lt;/p&gt;
&lt;p&gt;There’s also a lot more that would need to be customizable than you might expect. Some things that clients want tailored for their needs are: UI theme or individual UI elements; menu structures; field names and error messages; message/email/notification templates; access control rights; form layouts, what fields are included in each form, field validation logic; reports, what fields are included, and even retrieval logic. These are just some examples, there are many more that are difficult to foresee just how the clients might want them customized, so most likely even with the best-designed system there will still need to be some level of customization done for each client.&lt;/p&gt;
&lt;p&gt;How to avoid such pitfalls? It helps if your product has a strong identity and design from the very start, but that means having a strong product manager or management team with a good understanding of the domain required for your system. The product manager will need to manage the clients’ requirements and the direction of future development. The product manager has to be able to assess the client requests and say “no, that’s not how our product works,” when faced with requests that will make things more complicated for the development team.&lt;/p&gt;
&lt;p&gt;The above are drawn from my personal experience and from a few other developers’. I’m sure there are ways to handle product development to avoid these problems of course, but I believe that far more projects encounter these difficulties than do not, but it mostly comes down to management understanding the concerns and making adjustments to resources and schedules accordingly.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Naked CSS Day
</title>
            <link>https://mirror.roytang.net/2020/04/naked-css-day/</link>
            <pubDate>Thu, 09 Apr 2020 11:44:35 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/04/naked-css-day/</guid>
            <description>
            
            &lt;p&gt;It&amp;rsquo;s &lt;a href=&#34;https://css-naked-day.github.io/&#34;&gt;naked CSS day&lt;/a&gt;, so the site looks super bare-bones today! Inspired by &lt;a href=&#34;https://laurakalbag.com/css-naked-day-2020/&#34;&gt;Laura Kalbag&lt;/a&gt; and &lt;a href=&#34;https://meyerweb.com/&#34;&gt;Eric Meyer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Trying out the site without the CSS made me realize how I wasn&amp;rsquo;t setting width and height for the svg icons I was using, so they became huge when the CSS was removed. I also rearranged the templates so the footer is at the end of the document (previously it was being put there by CSS). So at the very least this did lead to some fixes!&lt;/p&gt;
&lt;p&gt;(Hopefully I don&amp;rsquo;t forget to restore the CSS tomorrow!)&lt;/p&gt;
&lt;p&gt;Screenshot of the Naked CSS version for posterity:&lt;/p&gt;



&lt;img src=&#34;https://mirror.roytang.net/2020/04/naked-css-day/Screenshot_2020-04-09_hu76b8207d4381c4fec09ce7f0226e0ad9_1545365_300x0_resize_q75_box.jpg&#34; /&gt;



            </description>
        </item>
    
        <item>
            <title>Browsers and HTTP
</title>
            <link>https://mirror.roytang.net/2020/03/browsers-and-http/</link>
            <pubDate>Tue, 24 Mar 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/03/browsers-and-http/</guid>
            <description>
            
            &lt;p&gt;Drew Devault wrote a great post/rant about &lt;a href=&#34;https://drewdevault.com/2020/03/18/Reckless-limitless-scope.html&#34;&gt;the reckless limitless scope of modern web browsers&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I conclude that it is impossible to build a new web browser. The complexity of the web is obscene. The creation of a new web browser would be comparable in effort to the Apollo program or the Manhattan project.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For the past year or so, I&amp;rsquo;ve been thinking about contributing to an open source project. Haven&amp;rsquo;t gotten around to it yet, but I have looked at a few projects of interest. Preferably it&amp;rsquo;s something I use on an every day basis, so Firefox was one of my candidates since it was my web browser of choice. I decided to try it out last month; I followed the instructions on their site for checking out the source and building the code. I assumed there would be a lot of code, and that the build would be a nontrivial amount of time, but just checking out the source and creating a build took the better part of a day; I believe the build itself took somewhere around 4-6 hours (I didn&amp;rsquo;t really record how long it took, just left it running while I went out and did stuff.) I haven&amp;rsquo;t actually tried hacking on the code itself yet, because with that kind of build time, I don&amp;rsquo;t see how one can be productive. (I decided to run the build a second time as I write this post, maybe it speeds up on successive builds?) It&amp;rsquo;s kind of intimidating to get into.&lt;/p&gt;
&lt;p&gt;The point is: Drew is right, and modern browsers are massive pieces of software. The comparison to the Apollo or Manhattan programs may even be understated; even though your browser isn&amp;rsquo;t taking people to the moon or testing nuclear weapons, it&amp;rsquo;s probably using way more memory than those programs combined!&lt;/p&gt;
&lt;p&gt;Somewhat related: Great post recently over on the Cloudflare blog about the &lt;a href=&#34;https://blog.cloudflare.com/the-history-of-the-url/&#34;&gt;History of the URL&lt;/a&gt;. One can&amp;rsquo;t talk about the history of the URL without talking about the history of the internet of course, and this post let me learn a lot more about those early days of the internet. One of my key takeaways is that a lot of the design decisions around URLs and the early internet made by people like Tim Berners-Lee largely intended the web and the HTTP protocol to be used for serving text-based documents. It&amp;rsquo;s even in the name of the protocol: hyper&lt;em&gt;text&lt;/em&gt; transfer protocol.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s what it was in the early days. People put up documents and web pages, and other people read them and maybe posted comments or other input and that was it. But the modern web (and thus modern web browsers) have tacked on so much functionality onto the presumably text-based protocol. First we added the ability to serve images and videos, then whole applications, formerly the domain of native apps. Just looking at my commonly-used websites, the web browser is used these days for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;full-featured email applications (GMail, ProtonMail)&lt;/li&gt;
&lt;li&gt;a complicated social media interface with multiple streams that automatically update (Tweetdeck)&lt;/li&gt;
&lt;li&gt;watching videos, tv shows and movies (Youtube, Netflix)&lt;/li&gt;
&lt;li&gt;instant messaging with groups and voice chat (Messenger, Discord)&lt;/li&gt;
&lt;li&gt;rss feed reader (Inoreader)&lt;/li&gt;
&lt;li&gt;note taking and productivity (Trello, Notion)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With systems like Google Stadia, you can even play AAA games via the web browser now!&lt;/p&gt;
&lt;p&gt;In the early days of the internet, all of those would still be native apps running outside the browser but these days we overload the poor HTTP protocol to handle all of these functions. The modern browser these days is almost comparable to an entire operating system (and there are of course already ChromeOS and FirefoxOS to drive home this point).&lt;/p&gt;
&lt;p&gt;I understand full well the benefits of overloading the browser and the HTTP protocol to handle all of these applications, but the scope creep is definitely unsustainable. The high resource usage of modern browsers and web pages even moreso. Imagine a new web browser that cuts off support for the evolving W3C specs at a certain point to compete on performance and stability instead.&lt;/p&gt;
&lt;p&gt;How about one that&amp;rsquo;s &amp;ldquo;just a browser&amp;rdquo;, literally just for browsing documents. Maybe one without JavaScript support completely, if you open a page that has SCRIPT tags, it will prompt you to open the OS-level browsers like Firefox or Chrome? Many modern applications could probably work without JavaScript, except users would have to do without many of the bells and whistles we&amp;rsquo;ve come to expect from modern webapps. i.e. clicking &amp;ldquo;Refresh&amp;rdquo; instead of timelines auto-updating, clicking &amp;ldquo;Next&amp;rdquo; instead of having infinite scroll, and so on.&lt;/p&gt;
&lt;p&gt;The never-ending scope creep reminds me of the never-ending growth pursued by late-stage capitalism; it can&amp;rsquo;t be sustainable in the long run, and maybe we need to start looking for different models other than &amp;ldquo;the browser does everything and will eventually become the OS.&amp;rdquo; (At that point, everything will have been subsumed by JavaScript)!&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Pure CSS Spoilers
</title>
            <link>https://mirror.roytang.net/2020/03/pure-css-spoilers/</link>
            <pubDate>Thu, 05 Mar 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/03/pure-css-spoilers/</guid>
            <description>
            
            &lt;p&gt;Edit 2020/04/17: A month and a half later, I found &lt;a href=&#34;https://mirror.roytang.net/2020/04/pure-html-toggles/&#34;&gt;a better way to do this&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I previously had some post that had some &lt;a href=&#34;https://mirror.roytang.net/2018/10/october-2018-watching-lately&#34;&gt;content hidden via spoiler tags&lt;/a&gt;, using a custom Hugo shortcode. Since I&amp;rsquo;m an &lt;a href=&#34;https://mirror.roytang.net/2020/02/old-web/&#34;&gt;old-school developer&lt;/a&gt; I was previously doing this using some Javascript run on load:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelectorAll&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.spoiler_header&amp;#34;&lt;/span&gt;);
    Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;el&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;) {
        &lt;span style=&#34;color:#a6e22e&#34;&gt;el&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addEventListener&lt;/span&gt;( &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;click&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;( &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt; ) {
            &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;nextEl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;el&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;nextElementSibling&lt;/span&gt;;
            &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;display&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getComputedStyle&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;nextEl&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;display&amp;#39;&lt;/span&gt;];
            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;display&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;none&amp;#39;&lt;/span&gt;) {
                &lt;span style=&#34;color:#a6e22e&#34;&gt;nextEl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;style&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;display&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;block&amp;#39;&lt;/span&gt;;
            } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
                &lt;span style=&#34;color:#a6e22e&#34;&gt;nextEl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;style&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;display&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;none&amp;#39;&lt;/span&gt;;
            }
        }, &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;);        
    });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the modern age however. We should really be trying to minimize our Javascript on the non-app public web as much as possible, so I decided to change it to a Pure CSS solution. Here&amp;rsquo;s a quick demo:&lt;/p&gt;
&lt;details class=&#34;spoiler&#34;&gt;
    &lt;summary&gt;(Spoilers)&lt;/summary&gt;
    This is hidden text!
&lt;/details&gt;
&lt;p /&gt;
&lt;p&gt;The Pure CSS solution uses the following HTML (including Hugo shortcode macros, should be easy to understand; .Inner is the actual spoiler content.):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;spoiler_container&amp;#34;&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;spoiler_link&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#spoiler-{{ $id }}&amp;#34;&lt;/span&gt;&amp;gt;(Spoilers)&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt; 
    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;spoiler_target&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;spoiler-{{ $id }}&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;spoiler&amp;#34;&lt;/span&gt;&amp;gt;{{ .Inner }} &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hide_spoiler&amp;#34;&lt;/span&gt;&amp;gt;Hide Spoilers&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;    
&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The CSS for this toggle is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;spoiler&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;none&lt;/span&gt;;
}
.&lt;span style=&#34;color:#a6e22e&#34;&gt;spoiler_target&lt;/span&gt;:&lt;span style=&#34;color:#a6e22e&#34;&gt;target&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;position&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;fixed&lt;/span&gt;;
}
.&lt;span style=&#34;color:#a6e22e&#34;&gt;spoiler_target&lt;/span&gt;:&lt;span style=&#34;color:#a6e22e&#34;&gt;target&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; .&lt;span style=&#34;color:#a6e22e&#34;&gt;spoiler&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;inline&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Basically, this takes advantage of the &lt;code&gt;:target&lt;/code&gt; CSS pseudo-selector. The &amp;ldquo;(Spoiler)&amp;rdquo; anchor changes the URL hash to point to the id of the &lt;code&gt;spoiler_target&lt;/code&gt; element, and we use the CSS &lt;code&gt;+&lt;/code&gt; operator to display the actual spoiler content when the &lt;code&gt;:target&lt;/code&gt; pseudo-selector is activated. I originally tried it with the id being assigned directly to the spoiler content, but this had the problem of &amp;ldquo;jumping around&amp;rdquo;, i.e. clicking the &amp;ldquo;(Spoiler)&amp;rdquo; link would scroll the page to the anchor location. The workaround for this was separating the target anchor from the spoiler content, making the anchor hidden (no text) and adding &lt;code&gt;position:fixed&lt;/code&gt; (to prevent the scrolling). The &amp;ldquo;Hide&amp;rdquo; link simply sets the URL hash location back to &amp;ldquo;#&amp;rdquo;.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Burnout
</title>
            <link>https://mirror.roytang.net/2020/02/burnout/</link>
            <pubDate>Thu, 06 Feb 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/02/burnout/</guid>
            <description>
            
            &lt;p&gt;In the middle of 2015, after 12ish years of working at the same company, I said to myself &amp;ldquo;I think I&amp;rsquo;m burnt out.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I remember that after a few days of ruminating over it I finally decided and said out loud to myself &amp;ldquo;That&amp;rsquo;s it, I&amp;rsquo;m done.&amp;rdquo; By the end of the year I had left the company. I didn&amp;rsquo;t write about the burnout then, out of respect for my former employer. But it&amp;rsquo;s been a while, I think the time has come to write about it.&lt;/p&gt;
&lt;p&gt;How do I know I was burnt out? I don&amp;rsquo;t believe there is any generally accepted consensus of symptoms for burnout, but I had a good set of indicators:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;exhaustion, both physical and mental. More and more often I found myself simply &lt;a href=&#34;https://mirror.roytang.net/2016/08/some-days-you-are-tired/&#34;&gt;tired&lt;/a&gt; of everything.&lt;/li&gt;
&lt;li&gt;frustration with all the problems at work and the projects I was assigned to, frustration that we kept running into the same issues again and again (mostly relating to scheduling and deadlines and resources), and frustration that any concerns I raised were not being addressed&lt;/li&gt;
&lt;li&gt;thinking about work all the time, even on my off hours I would be mentally considered the open Jira tickets, etc&lt;/li&gt;
&lt;li&gt;a feeling of helplessness in the face of overwhelming and seemingly never-ending project issues, re:scheduling, resources, quality, and so on&lt;/li&gt;
&lt;li&gt;a general feeling that that the effort I was putting in wasn&amp;rsquo;t worth it or wasn&amp;rsquo;t appreciated as much as I&amp;rsquo;d have liked&lt;/li&gt;
&lt;li&gt;general lack of free time, and even when there was free time, lack of energy to pursue anything productive&lt;/li&gt;
&lt;li&gt;permanently broken sleep cycle (a problem I still struggle with to this day)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The work at my former employer had always been&amp;hellip; challenging, to say the least. Overtime was not uncommon, and many people often quit citing the heavy workload. And yet I was generally happy/content working there for the better part of a decade. I kind of enjoyed and thrived in the environment most of the time, especially when facing technical challenges. Things got worse for me personally starting around 2014ish I think. There were a variety of challenges during this time, some of them due to the company/environment and some due to my own personal shortcomings.&lt;/p&gt;
&lt;p&gt;Environment challenges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a large project with constantly changing requirements and overly aggressive schedules (due to sales/marketing push) and almost continuous crunch. It felt like we were always jumping from one crisis to the next&lt;/li&gt;
&lt;li&gt;needing to provide support for demo and live systems during off hours, due to the target clients being halfway across the world&lt;/li&gt;
&lt;li&gt;some clients and coworkers were challenging to work with&lt;/li&gt;
&lt;li&gt;during this period, the traffic situation in Metro Manila had gotten significantly worse, draining even more time and energy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Personal challenges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I have a very strong sense of duty and responsibility. I realize this doesn&amp;rsquo;t sound like a negative, but it&amp;rsquo;d kind of a double-edged sword in that I take my duties as an employee very seriously and will sometimes sacrifice my own well-being if I think it is needed. For the longest time, one of &lt;a href=&#34;https://mirror.roytang.net/2012/08/232395423602573312/&#34;&gt;my favorite quotes has been&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Death is lighter than a feather, but Duty is heavier than a mountain” - Japanese proverb&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I find this quote symptomatic of the &amp;ldquo;hustle culture&amp;rdquo; that tends to lead to burnout. That&amp;rsquo;s probably a topic for another time, but the gist of it is that most companies and projects are a never-ending well of work that needs to be done, so much so that oftentimes the reward for good work is often more work. i.e.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/10/1185221831990464513/&#34;&gt;&amp;ldquo;The price for being the best is always having to be the best&amp;rdquo;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This &amp;ldquo;sense of duty&amp;rdquo; was so strong that even when I realized I was burning out, I still gave half a year&amp;rsquo;s notice for resignation, because I at least wanted to make sure everything was fine before I left. (Spoilers: it would take them a couple more years to actually finish the project). Someone reminded me recently that I expressed regret at leaving &amp;ldquo;in wartime&amp;rdquo; since the project got even further delayed and many of the issues were unresolved at that time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;related to the above, I also had a strong sense of pride. I considered myself very good at my job (justifiably so, based on annual reviews etc) and somehow had the mentality that I needed to be able to carry the weight of the project by myself. Whenever there was a resource or scheduling problem, one of my first instincts was to be to offer to take on the needed load myself, no matter how busy I already was.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Somewhat related to the above, but I also had (and often still have) an obsessive-compulsive need to read and reply to all emails and messages and notifications immediately, even outside of work hours&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;coupled with my strong sense of duty is a lack of ability to say no. As an example, for some reason even after I realized I was burnt out and notified the company I was leaving, I agreed to go live in another country on-site for three months. While it was good to experience another country, the work-life balance while I was there was even worse because the time zone problem still existed (except in reverse, i.e. the dev team was now on the other end of the world), and now I became a bottleneck (since I was one of the only two people on-site) and had to dedicate more time to bridging the gap.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As noted, I think many of these issues existed even before, but perhaps most of them were exacerbated by the fact that we now had to provide support across two time zones seven hours apart. That really took a toll on my work-life balance. Looking back at my emails during that period, I would often be replying late into the night or even early in the morning. I think it was really a question of energy and time being in short supply. I almost burnt myself out again in 2018, but at least I had the sense to recognize it faster and to step back as needed.&lt;/p&gt;
&lt;p&gt;I must point out that I bear no ill will towards my former employer or the people who worked there. There was a lot of room for improvement but everyone was just trying the best with what they were given. And I share some of the blame since I was kind of in a leadership position &amp;ndash; I should have pushed harder to make things better for everyone involved.&lt;/p&gt;
&lt;p&gt;How to avoid burnout:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;set reasonable boundaries between work time and personal time. Having a strong sense of duty is probably fine, as long as you know where that ends and to prioritize your own well-being as needed. Guard your free time zealously. As much as possible, don&amp;rsquo;t accept calls/messages outside of agreed upon work hours. The occasional exception for an emergency is probably fine, but don&amp;rsquo;t let it become a regular thing. Having well-defined boundaries allows your brain to avoid overlaps - if you&amp;rsquo;re at home, you should get used to not thinking about work.&lt;/li&gt;
&lt;li&gt;understand your own value and your own limits. Having a good understanding of what you bring to the team and the limits of your own ability and capacity means you can more effectively allocate your time and energy.&lt;/li&gt;
&lt;li&gt;always be aware of the employee-employer relationship. It&amp;rsquo;s okay to have a good working relationship with your superiors and coworkers and even to be friends with them, but never lose sight of the fact that to the employer, you are an employee. You aren&amp;rsquo;t &amp;ldquo;family&amp;rdquo; no matter what the company says. You are being paid to do a job, and that job has limits. You don&amp;rsquo;t owe the company your personal time or your absolute loyalty. It&amp;rsquo;s a capitalist enterprise, not a nation. Remember that in the end, the company will need to look out for itself (especially for publicly-traded companies), so you will need to look out for yourself as well.&lt;/li&gt;
&lt;li&gt;learn to say no. This is coupled with the first two points above. If you know what your boundaries are and what your limits are, it will become easier to say &amp;ldquo;No&amp;rdquo; to unreasonable requests. And remember that your personal time is your own, you don&amp;rsquo;t need to defend or explain why you don&amp;rsquo;t want to work on weekends or such&lt;/li&gt;
&lt;li&gt;regularly reflect on how things are going. Check your energy levels to make sure they aren&amp;rsquo;t constantly low and that you&amp;rsquo;re getting enough recharge time. If you think are starting to burn out, pull back, take some time off, and reassess how to move forward. Assess also how the company is addressing your concerns, and if they aren&amp;rsquo;t or are unwilling to do so, consider whether it may be time to move on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For people in leadership/management positions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;understand the value and capabilities of your team and build them up. One of the best things about working in larger companies/projects/teams is that you have other people to back you up, to support you and handle things when you reach your limits. Understanding both your own limits and the team&amp;rsquo;s limits allow you to better allocate tasks to avoid burnout.&lt;/li&gt;
&lt;li&gt;it isn&amp;rsquo;t sufficient to simply be asking your team members &amp;ldquo;Are you still ok?&amp;rdquo;, because if they&amp;rsquo;re anything like me, they may not be aware that they are starting to burn out already. Pay attention to how they are performing and how much time, effort and energy they are putting them in, and don&amp;rsquo;t push them unnecessarily.&lt;/li&gt;
&lt;li&gt;if your team members raise a concern, make sure to not only listen but to make them know what is being done to address their concerns. Unaddressed problems lead to frustration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In general, software development is a demanding industry, and it tends to attract people like me - industrious people who enjoy and take pride in their work. (Hence the famous quote about laziness, impatience and hubris.) Add to that the common difficulties in estimation, scheduling and planning, and it&amp;rsquo;s easy to understand why burnout is rampant in this industry. All the more reason for everyone involved to be aware of the risks of burnout and how to avoid it, and perhaps to somehow help reshape the industry to better avoid it.&lt;/p&gt;


            </description>
        </item>
    
        <item>
            <title>Tales from the Old Web (Development)
</title>
            <link>https://mirror.roytang.net/2020/02/old-web/</link>
            <pubDate>Tue, 04 Feb 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/02/old-web/</guid>
            <description>
            
            &lt;p&gt;There was this great (and long!) &lt;a href=&#34;https://eev.ee/blog/2020/02/01/old-css-new-css/&#34;&gt;article that came out recently about the history of CSS&lt;/a&gt;. It reminded me a lot of the old days when I started out in web development. So join me in a walk down memory lane as I reminisce about the trials and tribulations of early web development. (This one isn&amp;rsquo;t about CSS as much as that linked article.)&lt;/p&gt;
&lt;p&gt;I started out with web applications during the heyday of the much-maligned Internet Explorer 6, and pretty much the start of the age of webapps. Most of our projects during this time involved government agencies wanting to upgrade from their old existing client-server style systems. Those were often written in programming languages/tools like Delphi, Oracle Forms, PowerBuilder and such. That meant there was a lot of pressure on the web applications we developed to be able to match most of the client-side functionality available in those kinds of programs, which was often a challenge due to the limitations of the HTTP model.&lt;/p&gt;
&lt;h3 id=&#34;activex&#34;&gt;ActiveX&lt;/h3&gt;
&lt;p&gt;Typically, the government agencies at that time used only Internet Explorer in their workstations, so we could write our web applications to target just IE specifically. This meant we could use things like ActiveX to attempt to do things browsers normally can&amp;rsquo;t do. Some of it was for security purposes, like confirming the user&amp;rsquo;s authentication matches the current workstation user, or for Single-Sign On purposes, things like that. There were also some for interfacing with things outside the web browser, like document scanners.&lt;/p&gt;
&lt;p&gt;We also had to use some ActiveX to get around browser quirks. For example, IE6 had this problem with combo boxes/select elements where the width of the drop window would always match the width of the &lt;code&gt;select&lt;/code&gt; element, but this means if the &lt;code&gt;option&lt;/code&gt; text was longer than that width, they would not be readable. We had to make an ActiveX component just to fix this, by making the drop window wider on document load! Sample screenshot recovered from the vaults:&lt;/p&gt;











&lt;a href=&#34;#f47a745a95a668a6f94a9b0ba34a768e-lightbox&#34;&gt;
    &lt;figure&gt;
      &lt;img src=&#34;https://mirror.roytang.net/2020/02/old-web/Combo_hu3c773b5aac8489cf2c66b31c0ac0d078_147716_300x0_resize_q75_box.jpg&#34; alt=&#34;&#34; title=&#34;&#34; class=&#34;tn&#34; /&gt;
      &lt;figcaption&gt; (Click to view full-size)&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/a&gt;
&lt;div class=&#34;lightbox&#34; id=&#34;f47a745a95a668a6f94a9b0ba34a768e-lightbox&#34; style=&#34;display: none;&#34;&gt;
  &lt;a href=&#34;#_&#34;&gt;
    &lt;img src=&#34;https://mirror.roytang.net/2020/02/old-web/Combo.jpg&#34; /&gt;
  &lt;/a&gt;
  &lt;div class=&#34;lightbox_overlay&#34;&gt;
    &lt;p&gt;&lt;/p&gt;
    &lt;time class=&#34;dt-published&#34; datetime=&#34;4 Feb 2020 12:00am&#34;&gt;4 Feb 2020 12:00am&lt;/time&gt;&lt;a href=&#34;#_&#34;&gt;Close&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Some other ActiveX uses were to do things that would later on become standard features in browsers. Some other examples I can recall:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;spellchecking. We used an ActiveX component from a company called &lt;a href=&#34;https://www.wintertree-software.com/dev/wspell/info.html&#34;&gt;WinterTree software&lt;/a&gt;. I was surprised to find this particular product of theirs is still available, when practically all modern browsers have spellcheck support! I suppose it&amp;rsquo;s still useful for legacy environments maybe?&lt;/li&gt;
&lt;li&gt;we had this one project that needed a complicated graphing diagram. Think about those pinboards with pictures and documents connected by wires that conspiracy theorists use in memes, something like that. These days that kind of thing can be done using modern HTML+Javascript only, but during the IE6 days we were still pretty limited. We used another third-party ActiveX (can no longer recall the component name), that we wrapped in our own Delphi program for customization purposes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As far as I can tell, we ended up using ActiveX in some projects up to the early 2010s.&lt;/p&gt;
&lt;h3 id=&#34;gmail-and-ajax&#34;&gt;GMail and Ajax&lt;/h3&gt;
&lt;p&gt;Our initial webapps also did the thing where we would reload the page whenever doing something like going to the next page of results. This was pretty bad user experience-wise, especially compared to the client-server programs of old, which felt a lot more snappy since they were only loading the database results via the database driver, instead of an entire HTML page. By 2005, we were already using Ajax/XMLHttpRequest to get around these limitations in IE6. (&lt;a href=&#34;https://en.wikipedia.org/wiki/XMLHttpRequest&#34;&gt;The wikipedia entry&lt;/a&gt; says XHR didn&amp;rsquo;t officially exist in IE6, so I&amp;rsquo;m not sure how we did that, but I&amp;rsquo;m pretty sure we did). Our main use was for paging search result grids, but we weren&amp;rsquo;t even fetching XML or JSON back then, the XHR response would be the grid HTML itself, which we just plop right where the previous page was.&lt;/p&gt;
&lt;p&gt;I remember I was talking to the SA for the project about ajax, he asked what it was and why we were using it, my explanations was:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ajax is &amp;ldquo;Asynchronous Javascript and XML&amp;rdquo;; it&amp;rsquo;s a method of using
Javascript and XML data that allows us to make server-side (database)
calls without having to resubmit the entire page.&lt;/p&gt;
&lt;p&gt;We use it because it&amp;rsquo;s cool =p With Ajax, we can do something like allow
the user to re-sort a grid of records without reloading the rest of the
page. It is easier for us to meet the performance reqt.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve seen the Google Maps or GMail webapps, they both use Ajax.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The response was (unedited):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&amp;rsquo;ve used Gmail.  AJas is client side library??? Seems they process very
fast.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(I would later kind of &lt;a href=&#34;https://mirror.roytang.net/2006/05/gmail-raises-the-bar-for-everyone/&#34;&gt;regret using Gmail as a comparison&lt;/a&gt;!)&lt;/p&gt;
&lt;h3 id=&#34;cross-browser-development&#34;&gt;Cross-browser development&lt;/h3&gt;
&lt;p&gt;Most of our projects were for internal systems where the workstations had controlled environments so we were free to target a single browser only (usually IE). Some projects also had a public-facing portal though, and that required we do some cross-browser frontend stuff. Early on, the typical targets other than IE where Netscape and Firefox. This is where the IE6 compatibility headaches come in. It was usually a game of write the code for IE first, then let the testers find any NS/FF issues, then quash accordingly.&lt;/p&gt;
&lt;p&gt;We started checking in Firefox around the quirks mode era, as described in the article linked at the start of this post. The biggest challenge really was getting everything pixel-perfectly the same between browsers. The typical problems were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;browser differences in CSS models (the famous box model issue)&lt;/li&gt;
&lt;li&gt;differences in native controls (typically the &lt;code&gt;select&lt;/code&gt; and &lt;code&gt;input type=&#39;file&#39;&lt;/code&gt; controls were the more problematic ones)&lt;/li&gt;
&lt;li&gt;for cases where we needed ActiveX plugins for IE6, we would also need to develop corresponding FF add-ins, usually targeting Firefox 2.x (It&amp;rsquo;s fun to think about how the current version of Firefox is 70 major versions ahead of that!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We did very little cross-browser work at the start, with the proportion accelerating in tune with Firefox/Chrome&amp;rsquo;s rise in marketshare. By around 2010, it was common for projects to be testing across at least 2 versions of IE (6 and 7), 2 versions of Firefox (2 and 3), Google Chrome, and often Safari as well. (We didn&amp;rsquo;t really start thinking about Mac users until around 2007ish).&lt;/p&gt;
&lt;p&gt;Google Chrome came out in 2008, I found some old comments I made to our internal tech team when it came out:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have an installer in t:\roy&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not an OS, but it runs browser tabs in separate processes (literally&amp;hellip;each one has a different PID), such that a single webapp won&amp;rsquo;t crash everything else.&lt;/p&gt;
&lt;p&gt;More info here: t:\keis\Google_Chrome.pdf&lt;/p&gt;
&lt;p&gt;If it gets widespread adoption, it may affect the webapps we develop - it uses Webkit as a rendering engine (used by Safari), so it&amp;rsquo;s different from Firefox and IE. Haven&amp;rsquo;t encountered any problems yet though.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I also tested the memory usage of Chrome vs FF back then:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Try this:&lt;/p&gt;
&lt;p&gt;Firefox - open 9 tabs. Check the memory consumption. Close 8 tabs. Check
the memory consumption. (Remains around the same as previous one)&lt;/p&gt;
&lt;p&gt;In Chrome, the memory usage of closed tabs is 0.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a far cry from Chrome&amp;rsquo;s modern reputation as a memory hog! (Although I guess that reputation is probably related to people keeping too many tabs open.)&lt;/p&gt;
&lt;p&gt;Side story: while looking up old files for this post, I came upon a thread where we were discussing printing issues across browsers, and there was a problem in printing some pages from the browser because of limitations in IE6, and some management higher-up chimed in with a suggestion to &amp;ldquo;just tell the client to use IE7&amp;rdquo; as if the client will magically upgrade all their workstations for that one problem. :shrug:&lt;/p&gt;
&lt;h3 id=&#34;jquery&#34;&gt;jQuery&lt;/h3&gt;
&lt;p&gt;As the linked article mentioned, jQuery raised the bar when it came out, allowing developers to easily paper over cross-browser JavaScript concerns and providing an API that became so prevalent, years later parts of it would become built-in to modern browsers (i.e. &lt;code&gt;document.querySelector&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;I shared the link to &lt;a href=&#34;https://mirror.roytang.net/2006/01/jquery-new-wave-javascript/&#34;&gt;jQuery via delicious&lt;/a&gt; in 2006, but for sure we weren&amp;rsquo;t using it yet in our projects at that time. Based on my own CV, the first time I used it in a project was in 2009. It quickly became part of our standard libraries. I still remember in one particular project I spent some time thrashing out a bunch of issues because we needed to upgrade from like jQuery 1.3 to 1.4? Or something like that. Normally we&amp;rsquo;d fix to a certain version, but I think we needed to upgrade because of a jQuery plugin we needed. This was before the modern frontend development frameworks where you already have dependency management tools, so we had to make sure none of our other third-party JS components would break with the upgrade. The company ended up using jQuery as a standard in projects up to the time I left in 2015. (I&amp;rsquo;m told they later on switched to Vue.js, so more power to them.)&lt;/p&gt;
&lt;p&gt;I identify as a &amp;ldquo;full stack developer&amp;rdquo;, but these days the &amp;ldquo;frontend&amp;rdquo; half of that usually implies usage of one of the modern frontend frameworks: Angular, React, or Vue. While I am cognizant of those frameworks and have tried them all in some form or another over the past few years, my frontend development experience is still largely defined by the jQuery era. So when setting up personal projects or quick prototypes that&amp;rsquo;s still what I default to.&lt;/p&gt;
&lt;h3 id=&#34;css&#34;&gt;CSS&lt;/h3&gt;
&lt;p&gt;The linked article was about CSS, so I figured I should have some CSS stories too, but I find that I don&amp;rsquo;t have much, other than the cross-browser compat above. My main issue with CSS back then was that in a large project with a nontrivial amount of developers each working on separate screens, the CSS style sheets tended to get longer and denser with time. And with bug fixes and whatnot, it would tend to be full of hacks and workarounds like randomly placing &lt;code&gt;!important&lt;/code&gt; in so many places. I just assumed we had a whole bunch of CSS selectors that were no longer necessary but no one would bother cleaning up the style sheets because who knows what you&amp;rsquo;ll break? Basically a whole chaotic mess. IDK if modern tooling handles this because I haven&amp;rsquo;t been on larger projects since leaving the company, but I suppose the stronger delineation/specialization between frontend and backend dev work means less people to mess up the stylesheets, and those who do handle the styles are more aware of such pitfalls.&lt;/p&gt;
&lt;h3 id=&#34;thats-it-for-now&#34;&gt;That&amp;rsquo;s it (for now)&lt;/h3&gt;
&lt;p&gt;Well, this was a fun trip down memory lane. This post is already longer than I thought it would be, but those are all the old-timey stories I can recall right now. There&amp;rsquo;s a good chance there&amp;rsquo;s some more of them lodged in my brain somewhere that will come out sooner or later, so maybe there&amp;rsquo;s a part 2 sometime in the future.&lt;/p&gt;


&lt;img src=&#34;https://mirror.roytang.net/2020/02/old-web/Combo_hu3c773b5aac8489cf2c66b31c0ac0d078_147716_300x0_resize_q75_box.jpg&#34; /&gt;



            </description>
        </item>
    
        <item>
            <title>Flask vs Django
</title>
            <link>https://mirror.roytang.net/2020/01/flask-vs-django/</link>
            <pubDate>Fri, 24 Jan 2020 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2020/01/flask-vs-django/</guid>
            <description>
            
            &lt;p&gt;In a bid to reduce the number of webapps actually running on my server (for resource consumption reasons), I decided to migrate a small Flask app I had and merge into this larger Django app where I have a lot of my personal data tracking stuff. The Flask app was small enough, mostly containing backend support for this blog (like search and comment submissions) and some Twitter things. The migration was straightforward, taking around half a day, most of that was wrangling with Twitter API rate limits.&lt;/p&gt;
&lt;p&gt;It got me thinking, why did I build this using Flask in the first place? And that led to the more general question: when do I prefer using Flask over Django?&lt;/p&gt;
&lt;p&gt;Personal experience: I&amp;rsquo;m much more familiar with Django than Flask, having started using it back in 2008. Professionally, I&amp;rsquo;ve also worked on more Django projects, compared to only one Flask project that actually made it to production.&lt;/p&gt;
&lt;p&gt;The usual quick comparison is that Flask is the more lightweight web framework, suitable to smaller apps like microservices perhaps; while Django is the big monolithic web framework with all the bells and whistles already available. In practice this means Django already provides a lot of functionalities out of the box, while with Flask you have to install and figure out extensions for even the most common tasks like database management. A negative for Flask, but partly due to my inexperience: if you were already well-versed in the use of those extensions, I imagine getting everything together and setting them up will be easier.&lt;/p&gt;
&lt;p&gt;If there is need for a traditional RDBMS, I will probably use Django. I&amp;rsquo;m not super familiar with SQLAlchemy, the usual recommended ORM for Flask, so that&amp;rsquo;s a big factor. That being said, if I needed to work with something like a NoSQL database or some other nontraditional backend, it might be better to consider Flask since Django isn&amp;rsquo;t super flexible in that record - most of the pluses are oriented towards RDBMS-backed models.&lt;/p&gt;
&lt;p&gt;Admin: The built-in Django admin is a big plus for me; it means that for things like simple record CRUD, if usability and presentation isn&amp;rsquo;t a big concern, I can just use the Django admin as my backend without needing to write maintenance screens. There is an equivalent &lt;a href=&#34;https://flask-admin.readthedocs.io/en/latest/&#34;&gt;Flask Admin plugin&lt;/a&gt;, but I&amp;rsquo;ve not tried that either.&lt;/p&gt;
&lt;p&gt;API building: I&amp;rsquo;m already familiar with the Django REST framework, I&amp;rsquo;m not sure if there&amp;rsquo;s an equivalent for Flask. The one Flask project we did, we rolled our own API backend.&lt;/p&gt;
&lt;p&gt;I find that I tend to favor Flask when building quick proof-of-concept apps that don&amp;rsquo;t need a database. An example that was part of the app I migrated recently would be this quick &lt;a href=&#34;https://apps.roytang.net/twitter/&#34;&gt;Twitter app I wrote about a year ago&lt;/a&gt;, to find intersections between follows/followers. It didn&amp;rsquo;t need a database, only twitter API calls. (I have since merged it into the Django app, so the current version is using Django now.)&lt;/p&gt;
&lt;p&gt;In general I prefer Django, but that is probably due to familiarity and experience. I would love to have a full-scale project that uses Flask though! Perhaps in the future, for now all my personal projects are running on Django.&lt;/p&gt;



&lt;img src=&#34;https://mirror.roytang.net/2020/01/flask-vs-django/code_hu9effae59deb16cf77937c315ce614999_79312_300x0_resize_box_2.png&#34; /&gt;



            </description>
        </item>
    
        <item>
            <title>Is SQL a dying art?
</title>
            <link>https://mirror.roytang.net/2019/11/sql-dying-art/</link>
            <pubDate>Wed, 13 Nov 2019 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/11/sql-dying-art/</guid>
            <description>
            
            &lt;p&gt;I was helping my brother check some database issues the other week, and he mentioned how impressed he was with how quickly I was able to come up with SQL queries on the fly. I told him that SQL was one of the skills I considered myself to have mastery over. This shouldn&amp;rsquo;t be surprising given my early career path:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;for most of my first year working as a software developer, I was working on reports which involved lovingly handcrafted (and oftentimes quite complicated) SQL queries. And there was a lot of Oracle PL/SQL too&lt;/li&gt;
&lt;li&gt;by my second year, I was already conducting SQL training for new hires in the company&lt;/li&gt;
&lt;li&gt;by my third year and onwards, I was already regularly asked to help other projects with their SQL performance issues&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of my experience was with Oracle SQL, but as the ANSI standard has evolved quite well, the knowledge translates well to other databases. (Maybe not the PL/SQL though.)&lt;/p&gt;
&lt;p&gt;These days I feel like SQL knowledge has become a much rarer skill among developers, chiefly for 2-3 reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the introduction and widespread use of tools like frameworks with ORMs and business intelligence software, minimizing the amount of SQL that needs to be hand-written in most applications&lt;/li&gt;
&lt;li&gt;the modern software development stack is much more complicated than it used to be, particularly for web development. When I started out, almost everybody was a &amp;ldquo;full stack engineer&amp;rdquo;, and that meant having to be familiar with most technologies you were using, including the database layer. These days, for any nontrivial project, developers on a team will often need to be specialized, with many working only on frontend issues, some on backend, and any SQL or stored procedure work is handled by a specialist developer or a DBA.&lt;/li&gt;
&lt;li&gt;a possible secondary reason is the rise of alternative data storage solutions such as NoSQL databases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Realistically, the need for people who can write good SQL may never go away, but the poor of people who can do so may be getting smaller due to the reasons above. Well, maybe it&amp;rsquo;s a good thing for me, since scarcity means it may become a more valuable skill as time goes on!&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>What to do in a production crisis
</title>
            <link>https://mirror.roytang.net/2019/11/prod-crisis/</link>
            <pubDate>Tue, 12 Nov 2019 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/11/prod-crisis/</guid>
            <description>
            
            &lt;p&gt;Despite our best efforts as software developers, it can still happen: production goes down. Or some sort of bug introduces catastrophic data error. Hopefully you have a support/DevOps team to handle the response. If not, the dev team themselves have to step in. This usual means a mad rush to figure out what happened and how to fix it, sometimes during off hours and maybe even into the early morning, all while facing pressure from clients and higher-ups. I was advising another developer in such a situation a while back, and it was his first stint as technical lead so he was extra worried. I gave him a few tips to see him through the time of crisis.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Keep calm. In Tagalog, &lt;a href=&#34;https://mirror.roytang.net/2019/04/project-management-tip-kalma-lang/&#34;&gt;kalma lang&lt;/a&gt;. In a crisis, there can be a lot of stress and pressure to fix things immediately. It&amp;rsquo;s best to keep a level head and make sure the team is keeping it together. Don&amp;rsquo;t let your emotions carry you away, and try to avoid panic.&lt;/li&gt;
&lt;li&gt;Solve one problem at a time until you can go home. This is a lesson from the movie &lt;a href=&#34;https://mirror.roytang.net/2015/10/10154142633048912/&#34;&gt;The Martian&lt;/a&gt;. In a crisis it can be difficult to focus on the task at hand and if you&amp;rsquo;re not thinking straight you might just charge in there blindly and move things around until things get better. Follow step #1, then figure out what are the most pressing problems that need to be solved immediately, then address them one at a time. It can also help your team morale to identify which issues can be left until the next day. This gives them a concrete sense of what is set of problems to focus on, and motivates them to solve those so they can go home.&lt;/li&gt;
&lt;li&gt;Be extra careful. In times like these, you may find yourself needing to deploy hotfixes directly to production or maybe even update data directly in the production database, in the interest of expediency. It&amp;rsquo;s also during times like these that you are more likely to make a mistake, especially if your team is under stress and working overtime/during off hours. So it would pay to be extra careful, maybe add an additional layer of review before deploying things directly to production. You&amp;rsquo;re already in a crisis, don&amp;rsquo;t make things worse by making a sloppy mistake.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Having to provide tech support during such a crisis is something you shouldn&amp;rsquo;t wish on any developer, but sometimes it can&amp;rsquo;t be avoided. (Some might even consider it a kind of rite of passage so to speak). Make sure to have the right mindset to be able to operate well under such stressful conditions.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>DevNotes: Python&#39;s yield
</title>
            <link>https://mirror.roytang.net/2019/11/python-yield/</link>
            <pubDate>Thu, 07 Nov 2019 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/11/python-yield/</guid>
            <description>
            
            &lt;p&gt;I&amp;rsquo;ve been using Python for well over 10 years, and I still don&amp;rsquo;t have an intuitive mastery of one of its keywords: &lt;code&gt;yield&lt;/code&gt;. Everytime I see it in someone&amp;rsquo;s code I need to stop and mentally remind myself what it does. I figured I&amp;rsquo;d write a devnote to help improve my recall.&lt;/p&gt;
&lt;p&gt;Typically, &lt;code&gt;yield&lt;/code&gt; is used in a function with a loop, like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;some_func&lt;/span&gt;(lim):
    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, lim):
        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; i
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;yield&lt;/code&gt; means the function returns a &amp;ldquo;generator&amp;rdquo; that can be used as an iterable in a loop:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; val &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; some_func(&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;):
    &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(val)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also straight up convert the generator into a list via something like &lt;code&gt;list(some_func(5))&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The equivalent function, without using &lt;code&gt;yield&lt;/code&gt; would be something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;some_func&lt;/span&gt;(lim):
    result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, lim):
        result&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(i)
    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; result
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is definitely the kind of function I&amp;rsquo;ve written often! Now that I&amp;rsquo;ve actually written it down, &lt;code&gt;yield&lt;/code&gt; is rather straightforward, and hopefully it can help me shorten some of the python code I&amp;rsquo;ll be writing in the future!&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>The Perils of Handover Documentation
</title>
            <link>https://mirror.roytang.net/2019/11/handover-documentation/</link>
            <pubDate>Wed, 06 Nov 2019 12:07:37 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/11/handover-documentation/</guid>
            <description>
            
            &lt;p&gt;A while back I found myself having to figure out how to compile/build/run a mobile application. The developers previously assigned to the project were no longer available to consult with, but they did leave behind some documentation. However, their documentation quality left a lot to be desired. The instructions they left basically amounted to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ionic serve&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ionic codrova run android&lt;/code&gt;/&lt;code&gt;ios&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Okay, first sign of trouble is that their instructions were basically commands that anyone who knew the app used Ionic would be able to Google. But ok, I gave this a shot, even though I had not used Ionic/Cordova before, I assumed the simple documentation meant it would be straightforward (spoiler: it was not).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First problem: &lt;code&gt;npm install&lt;/code&gt; fails. Okaaaay. I dig around for a bit and figure out there&amp;rsquo;s an incompatibility between one of the packages and my node version (12). Some more digging around tells me that the previous developers likely were using Node 8. I switch to Node 8 and &lt;code&gt;npm install&lt;/code&gt; finishes successfully, somehow.&lt;/li&gt;
&lt;li&gt;Second problem: &lt;code&gt;ionic serve&lt;/code&gt; throws an error from one of the dependencies. A bit more digging around. Reading ionic docs. Stackoverflow. Turns out there was some extra configuration needed to be added if you&amp;rsquo;re doing this stuff on Windows. (I later found out the previous developers had used Ubuntu.)&lt;/li&gt;
&lt;li&gt;Third problem: &lt;code&gt;ionic serve&lt;/code&gt; throws a whole bunch of typescript errors. Why? After facepalming for a bit, I dig around again. And dig some more. Looks like dependency issues. But the &lt;code&gt;package.json&lt;/code&gt; has dozens of dependencies, it would take me forever to figure out the problem. I find myself digging through the repository&amp;rsquo;s commit history trying to figure out if there was a recent change to the &lt;code&gt;package.json&lt;/code&gt; that would cause the issue. What I notice instead is that a couple of weeks before the handover, someone deleted &lt;code&gt;package-lock.json&lt;/code&gt; from the repository for some unknown reason. Deciding to try it out, I restore the old version of &lt;code&gt;package-lock.json&lt;/code&gt;, do the whole &lt;code&gt;npm install&lt;/code&gt; dance again, and sure enough the typescript errors vanish. But we&amp;rsquo;re not yet done.&lt;/li&gt;
&lt;li&gt;Fourth problem: &lt;code&gt;ionic serve&lt;/code&gt; no longer throws an error, but when it launches the browser, there&amp;rsquo;s a cyclic error stacktrace shown. Some stackoverflow answers tells me this is because something can&amp;rsquo;t be serialize it, and I trace the problem to a generic error handler that just calls &lt;code&gt;console.log&lt;/code&gt;. I comment this out so that I can see the actual error, which is &amp;ldquo;cordova doesn&amp;rsquo;t exist&amp;rdquo; (or something to that effect). I realize this means that the mobile app uses some native-specific things, so Cordova is required and it&amp;rsquo;s not going to work when served via web, so why did they include this step?&lt;/li&gt;
&lt;li&gt;So instead I do &lt;code&gt;ionic cordova run android&lt;/code&gt; (after installing the necessary Android Studio/SDK things&amp;hellip;this was after my &lt;a href=&#34;https://mirror.roytang.net/2019/09/two-backups/&#34;&gt;recent computer troubles&lt;/a&gt;, so I had a fresh OS install.) And yes, long story short, I am finally able to get the Android app running in an emulator (and eventually in a device, and then the iOS version as well).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I recounted this because I could use it as lesson to note that even when you do have documentation, there are so many ways the documentation can be insufficient. In this case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the dependencies/requirements were not specified&lt;/li&gt;
&lt;li&gt;they did not have someone actually go through these steps from a fresh install to verify things would work&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A case could also be made that they should have just had something like a Docker setup available, but they actually did have a &lt;code&gt;docker-compose.yml&lt;/code&gt; in the repository! Sadly, it wouldn&amp;rsquo;t have worked either because it referenced some environment variables that I had no idea what the value needed to be. And besides, my PC doesn&amp;rsquo;t support Docker (silly Windows Home), but looking at the docker image did clue me in to what Node version to use.&lt;/p&gt;
&lt;p&gt;In this instance, it was also a problem was that the old team was asked to provide the documentation only after the handover (due to circumstances beyond our control). Ideally, a handover would have them available for consultation should there be problems found with their documentation, and the documentation would only be cleared for acceptance after such review. (Realistically, a lot of documentation like user manuals wouldn&amp;rsquo;t even be looked at, but in this case, the ability to build the application was kind of crucial!)&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Python: Markov Chains
</title>
            <link>https://mirror.roytang.net/2019/08/python-markov-chains/</link>
            <pubDate>Wed, 28 Aug 2019 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/08/python-markov-chains/</guid>
            <description>
            
            &lt;p&gt;Back when I was still learning Python in 2008, one of the first &amp;ldquo;fun&amp;rdquo; scripts I wrote was a text generator using &lt;a href=&#34;https://en.wikipedia.org/wiki/Markov_chain&#34;&gt;Markov chains&lt;/a&gt;. I&amp;rsquo;d run it against all the chat logs I had with people at work and serve the results from a webserver on my computer. THe results were often amusing and sometimes hilarious.&lt;/p&gt;
&lt;p&gt;Since I&amp;rsquo;ve been going through my old scripts lately, I thought I&amp;rsquo;d update that script to Python 3 (read: add parentheses around print params and use &lt;a href=&#34;https://mirror.roytang.net/2019/08/devnotes-python-pathlib/&#34;&gt;pathlib&lt;/a&gt;) and run it against all the posts on this here site. I added the script to my deploy script for this site (thus further worsening my build times), so it should generate a new page every hour or so. I also added the generated markdown file to gitignore so it never gets saved to the repo. Every output of this script will be fleeting and ephemeral!&lt;/p&gt;
&lt;p&gt;You can view the output &lt;a href=&#34;https://mirror.roytang.net/demos/markov&#34;&gt;here&lt;/a&gt;. You can view the script source &lt;a href=&#34;https://github.com/roytang/blog/blob/master/utils/markov.py&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Devnotes: Python Pathlib
</title>
            <link>https://mirror.roytang.net/2019/08/devnotes-python-pathlib/</link>
            <pubDate>Tue, 27 Aug 2019 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/08/devnotes-python-pathlib/</guid>
            <description>
            
            &lt;p&gt;Ever since I started learning Python back in 2008ish, I&amp;rsquo;ve been using it as my primary scripting language for various tasks such as processing log files, organizing my own file system, processing stuff on this blog, and so on. A lot of it is basically moving files around. In the days of Python 2, that involved a lot of imports of different libraries like &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;shutil&lt;/code&gt; and &lt;code&gt;glob&lt;/code&gt;. It can become a bit messy with so many imports, and I often can&amp;rsquo;t remember which import I need for a particular case and end up having to search for the documentation (or stackoverflow, let&amp;rsquo;s not kid ourselves here).&lt;/p&gt;
&lt;p&gt;With Python 3, a new cleaner option is available to replace all of the above libraries: &lt;a href=&#34;https://docs.python.org/3/library/pathlib.html&#34;&gt;Pathlib&lt;/a&gt;, introduced in Python 3.4, provides an object-oriented way of doing file operations, replacing many of the most common uses I had for the libraries above. Some sample usage:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pathlib &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; path

&lt;span style=&#34;color:#75715e&#34;&gt;# Declaring a path object is just passing the path string&lt;/span&gt;
p &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Path(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/home/roytang/stuff&amp;#34;&lt;/span&gt;)

&lt;span style=&#34;color:#75715e&#34;&gt;# Other paths can be derived using the / operator&lt;/span&gt;
subdir &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; p &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;favorites&amp;#34;&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# p.glob replaces stuff like os.walk and glob.glob&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; mdfile &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; p&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;glob(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;**/*.md&amp;#34;&lt;/span&gt;):
    &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(str(mdfile))

    &lt;span style=&#34;color:#75715e&#34;&gt;# path objects have properties for individual path elements&lt;/span&gt;
    filename &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mdfile&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name
    stem &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mdfile&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stem &lt;span style=&#34;color:#75715e&#34;&gt;# filename without extension&lt;/span&gt;

    &lt;span style=&#34;color:#75715e&#34;&gt;# path.exists() replaces os.exists()&lt;/span&gt;
    newdir &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; subdir &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; stem 
    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; newdir&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exists():
        &lt;span style=&#34;color:#75715e&#34;&gt;# path.mkdir replaces os.makedirs&lt;/span&gt;
        newdir&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mkdir(parents&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;True)

    newfile &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; newdir &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; newfile &lt;span style=&#34;color:#75715e&#34;&gt;# path objects can be either files or dirs&lt;/span&gt;
    &lt;span style=&#34;color:#75715e&#34;&gt;# for file copying, you still need shutil!&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; shutil
    shutil&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;copy(str(mdfile), str(newfile))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For me it&amp;rsquo;s much cleaner to use than the old methods and require less imports (most of the time). I eventually hope to migrate all my older scripts to use Pathlib (the same way I migrated them away from Python 2 a while back), and moving forward I plan to use it primarily for filesystem operations.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Devnotes: Migrating Mercurial to Git
</title>
            <link>https://mirror.roytang.net/2019/08/devnotes-migrating-mercurial-to-git/</link>
            <pubDate>Sat, 24 Aug 2019 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/08/devnotes-migrating-mercurial-to-git/</guid>
            <description>
            
            &lt;p&gt;Big news in online repositories this week is that &lt;a href=&#34;https://bitbucket.org/blog/sunsetting-mercurial-support-in-bitbucket&#34;&gt;Bitbucket is sunsetting support for Mercurial&lt;/a&gt;! This might be the death knell for Mercurial, although Git was already the super popular choice before. Back when I started using online source control for my personal coding projects I started out with Bitbucket over Github because they offered unlimited private repos and Mercurial (which I had already tried out before at work, so at first I preferred it over git). Now that Gitlab and Github both offer unlimited private repos, there&amp;rsquo;s no reason to stick with Bitbucket either. I had already migrated most of my private Git repos to Gitlab before, but hadn&amp;rsquo;t realized until now that I also had a couple of Mercurial repos there that needed to be migrated as well. I hadn&amp;rsquo;t touched those repos in so long that I didn&amp;rsquo;t even have Mercurial installed locally anymore! (Although I&amp;rsquo;m still using the code locally!)&lt;/p&gt;
&lt;p&gt;Luckily, converting a mercurial repo to git turned out to be straightforward thanks to a project called &lt;a href=&#34;https://repo.or.cz/fast-export.git&#34;&gt;fast-export&lt;/a&gt;. Modified instructions from a convenient &lt;a href=&#34;https://stackoverflow.com/questions/10710250/converting-mercurial-folder-to-a-git-repository#&#34;&gt;stackoverflow post&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;brew install hg &lt;span style=&#34;color:#75715e&#34;&gt;# install hg on my mac first&lt;/span&gt;
cd ~
hg clone https://user@bitbucket.org/user/reponame
git clone git://repo.or.cz/fast-export.git
git init git_repo
cd git_repo
~/fast-export/hg-fast-export.sh -r ~/reponame
git checkout HEAD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The process was fairly quick, and the commit history from the Mercurial side was even retained, I would have been happy just retaining HEAD! After that, I just create a new private repo on Gitlab, set that as the remote, then push to Gitlab as usual.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a good thing I remembered to check my Bitbucket account, as apparently they will be deleting the old Mercurial repos when time is up!&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Devnotes: TT Miniproject (Django Rest Framework, Unit Testing, VueJS, Geocoding, Nightwatch e2e Testing)
</title>
            <link>https://mirror.roytang.net/2019/08/devnotes-tt-miniproject/</link>
            <pubDate>Tue, 20 Aug 2019 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/08/devnotes-tt-miniproject/</guid>
            <description>
            
            &lt;p&gt;I recently found myself doing a really small project as sort of a proof of concept/demo for a potential client. It often seems that it might be a waste of time to do something like this since you don&amp;rsquo;t know if the project will actually push through or maybe the client will want something else. To kind of hedge my bets a bit, I decided to take the opportunity to try out some new technologies so that no matter what I at least learned something from all of this. (What is life if not learning?)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Django Rest Framework&lt;/strong&gt;. I&amp;rsquo;ve been on projects using DRF before, but it&amp;rsquo;s my first time building a full CRUD-style API from scratch using it. There is a little bit of a learning curve when it comes to views and serializers, but for the most part it&amp;rsquo;s straightforward. I did enjoy how the framework has out of the box support for the most common REST API scenarios, and there are available backends for different authentication methods. I used JWT Authentication, which isn&amp;rsquo;t part of the library by default, but already had a pretty good implementation available elsewhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Python Unit Testing&lt;/strong&gt;. I have dabbled &lt;a href=&#34;https://mirror.roytang.net/2019/02/triviastorm-text-and-answer-parsing/&#34;&gt;a little bit with Django&amp;rsquo;s Unit Test framework before&lt;/a&gt;, but not having worked with any company that does TDD I&amp;rsquo;ve never tried doing it the &amp;ldquo;test first&amp;rdquo; way. For reference I used an online book: &lt;a href=&#34;https://www.obeythetestinggoat.com/book/part1.harry.html&#34;&gt;Test Driven Development with DJango&lt;/a&gt;. I actually enjoyed the exercise, perhaps a little too much. My impression of TDD has always been that the value lies in how much courage it can give the developer - the courage to try new things without worrying about whether you have broken something that was working before. This support came in handy while I was trying to figure out some issues with DRF especially around permissions etc. Really helpful for backend APIs like this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Python Social Auth&lt;/strong&gt;. The proof-of-concept needed some social media logins. I used &lt;a href=&#34;https://github.com/python-social-auth/social-core&#34;&gt;Python Social Auth&lt;/a&gt; for this, really straightforward library with good documentation and supports a lot of backends. I ended up using Twitter and Github OAuth as those were the simplest to implement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VueJS&lt;/strong&gt;. The proof-of-concept needed to be a single-page app, so that meant a bunch of Javascript was needed. I willingly admit I&amp;rsquo;m still behind with the latest web frontend frameworks and trends. In my head I&amp;rsquo;m still living in a jQuery world. I dabbled a bit with React when trying out &lt;a href=&#34;https://mirror.roytang.net/2019/07/upgrading-a-react-native-project/&#34;&gt;React Native&lt;/a&gt;, but I wasn&amp;rsquo;t fond of it. For this project I decided to use &lt;a href=&#34;https://vuejs.org&#34;&gt;VueJS&lt;/a&gt; since I had skimmed through their documentation before and things seemed cool. I used it mostly for the dynamic parts and the data binding. I didn&amp;rsquo;t get too far into using components, my VueJS app was a huge monolith, which is apparently is frowned upon when using these frameworks lol. (Someone familiar with VueJS who looked at the code said it was &amp;ldquo;very strange&amp;rdquo;). I have barely scratched the surface of this framework, I will probably need to build something a bit more involved with it to get a better grasp. I will say that I enjoyed the VueJS syntax/structure better than React.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Google Maps API and Geocoding&lt;/strong&gt;. The miniproject had some components that required me to show stuff on a map and do some geocoding, so I had to set up the API for that. Google&amp;rsquo;s documentation is fairly good here. One thing I didn&amp;rsquo;t expect is that you need a Google Cloud account with a billing method in order to access the API. My understanding is I wouldn&amp;rsquo;t be charged for anything since I was just using the free tier, I hope that was accurate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nightwatch e2e Testing&lt;/strong&gt;. I&amp;rsquo;ve had some projects with end to end testing before, mostly via scripts or Selenium, but it&amp;rsquo;s my first time trying out one of these end to end testing frameworks by writing the scripts myself. I chose to go with Nightwatch, it was reasonably strightforward and worked on Mac and Windows. One thing I will note that initially gave me problems during set up is that using the ChromeDriver can be a bit finnicky on Windows. You have to specify the actual location of the executable in your nightwatch.json, like so: &lt;code&gt;&amp;quot;server_path&amp;quot;: &amp;quot;node_modules/chromedriver/lib/chromedriver/chromedriver.exe&amp;quot;,&lt;/code&gt; instead of just the &lt;code&gt;node_modules/.bin/chromedriver&lt;/code&gt; the documentation implies.&lt;/p&gt;
&lt;p&gt;All in all the mini-project didn&amp;rsquo;t take much work - maybe 2-3 mandays of effort at most, and I did learn a lot from it, so no matter the outcome it was already a win.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Devnotes: PostgreSQL on the command line
</title>
            <link>https://mirror.roytang.net/2019/08/devnotes-postgresql-on-the-command-line/</link>
            <pubDate>Thu, 08 Aug 2019 00:00:00 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/08/devnotes-postgresql-on-the-command-line/</guid>
            <description>
            
            &lt;p&gt;I decided to start doing small &amp;ldquo;devnotes&amp;rdquo; on developer stuff I&amp;rsquo;m doing so I can refer to them later (and also because I feel like I could use more technical content on this blog)&lt;/p&gt;
&lt;p&gt;Today is about PostgreSQL. I haven&amp;rsquo;t used it much beyond standard ANSI sql stuff. You won&amp;rsquo;t always have a graphical interface to access your database, sometimes you need to ssh to prod and query the database from the shell.&lt;/p&gt;
&lt;p&gt;The command line for PostgreSQL is &lt;code&gt;psql&lt;/code&gt;. You can do:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;psql [database-user-name] -d [database-name]&lt;/code&gt; and it should prompt you for your password.&lt;/p&gt;
&lt;p&gt;But when I tried this today, I got:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;psql: FATAL:  Peer authentication failed for user &amp;quot;[database-user-name]&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The problem had something to do with the permissions available to my ssh user. The workaround was to tell psql I was doing this from the local machine:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;psql: FATAL:  Peer authentication failed for user &amp;quot;[database-user-name]&amp;quot; -h 127.0.0.1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Worked fine.&lt;/p&gt;
&lt;p&gt;After connecting, you get a prompt that looks like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[database-name]=# &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now, when I connected today, I had to do some queries, but I wasn&amp;rsquo;t superfamiliar with the project&amp;rsquo;s schema, and would have to muck around to find table names and field names. The psql shell provides some handy shortcuts for that:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\dt&lt;/code&gt; - outputs a list of tables&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\d [table-name]&lt;/code&gt; - describes a specific table, showing field names&lt;/p&gt;
&lt;p&gt;You can also just directly run an SQL query, just make sure to end it with a semicolon.&lt;/p&gt;
&lt;p&gt;If the command outputs a lot of records, it will show them to you in a page-able vim-like format. You can recognize it when it stops with a &lt;code&gt;:&lt;/code&gt; after each page and you can hit spacebar to proceed, and at the end it shows &lt;code&gt;(END)&lt;/code&gt;. And like vim, you might not know how to get out of this view. I had to google it myself, you just have to hit &lt;code&gt;q&lt;/code&gt; to go back to the psql shell.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Upgrading a React Native Project
</title>
            <link>https://mirror.roytang.net/2019/07/upgrading-a-react-native-project/</link>
            <pubDate>Sat, 20 Jul 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/07/upgrading-a-react-native-project/</guid>
            <description>
            
            &lt;p&gt;I have a small mobile app that I wrote using React Native (henceforth RN) back in 2017, currently deployed on the Google Play Store and Apple App Store. Shortly before my US trip, I got an email from Google telling me about a required action:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By August 1, 2019, all apps that use native code must provide a 64-bit version in addition to the 32-bit version in order to publish an update. This past January, we reiterated that this is required in order to make way for innovation and in anticipation of future Android devices that only support 64-bit code.&lt;/p&gt;
&lt;p&gt;As the deadline approaches, we wanted to remind you that at least one of your apps* uses native code but does not currently offer a 64-bit variant&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This illustrates for me one of the big problems with mobile development: that app was perfectly fine when I last deployed it in 2018 and now even though I haven&amp;rsquo;t changed anything, there&amp;rsquo;s additional work that needs to be done. I guess I&amp;rsquo;m too used to web development where we just deploy the app on a server and the server architecture doesn&amp;rsquo;t suddenly change for no reason. With mobile apps, the platforms and APIs and frameworks change quickly, leading to situations where even if your app doesn&amp;rsquo;t have any pending changes, you need to do some work on it anyway. (Granted, this can also happen with webservers due to security patches and such, but with managed hosting, that&amp;rsquo;s not something you worry about as often, and the resolution is often straightforward!)&lt;/p&gt;
&lt;p&gt;As I was about to be out of the country on vacation for more than a month, I put this off and only started looking at it when I got back. Luckily, I did actually also have some minor revisions to do on the app, so I would do all the work in one go.&lt;/p&gt;
&lt;p&gt;As it turns out, the version of RN I was using was too old and did not support providing the 64-bit version. I had to upgrade to a later version of RN, and after reviewing the process, it seemed like a nontrivial amount of work. There was an upgrade tool, but because I was so many versions behind, it couldn&amp;rsquo;t work directly. The problem seems to be in the initial boilerplate created by &lt;code&gt;react-native init&lt;/code&gt; for the ios and android subprojects; the older boilerplate was out of date and needed to be updated, which was not straightforward given that you may have modified that boilerplate afterwards. I had to use some kind of diff tool to manually find the differences in the boilerplate and merge the changes manually.&lt;/p&gt;
&lt;p&gt;That seemed like a lot of work, and the app wasn&amp;rsquo;t that large and didn&amp;rsquo;t have that much code, so I figured it was more straightforward to just create a new RN project from scratch, which would be using the latest version of RN (0.60.3, from 0.50.4). Afterwards, I port over the project code into the new project, them swap the new project back into the old project&amp;rsquo;s repo. That way I only had to update the few dependencies I was using in the project itself.&lt;/p&gt;
&lt;p&gt;It was still a nontrivial amount of work, but I eventually got everything working through many iterations of trial-and-error.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I went ahead and upgraded the dependencies to the latest versions as well, to make sure there was no conflict with the new RN version&lt;/li&gt;
&lt;li&gt;ReactNavigation had some backward incompatible changes:
&lt;ul&gt;
&lt;li&gt;needed to &lt;a href=&#34;https://reactnavigation.org/docs/en/getting-started.html#installation&#34;&gt;initialize the gesture handler&lt;/a&gt; in the java class&lt;/li&gt;
&lt;li&gt;new function &lt;code&gt;createStackNavigator&lt;/code&gt; instead of just &lt;code&gt;StackNavigator&lt;/code&gt; constructor&lt;/li&gt;
&lt;li&gt;needed to use &lt;code&gt;createAppContainer&lt;/code&gt; to initialize the navigator&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;some components such as WebView and AsyncStorage were no longer part of RN core and had to be imported accordingly&lt;/li&gt;
&lt;li&gt;Android now requires that I explicitly prompt for the &lt;code&gt;READ_EXTERNAL_STORAGE&lt;/code&gt; permission when trying to access the camera roll&lt;/li&gt;
&lt;li&gt;Google now recommends that I provide an upload key for encryption, but since I reset the project, I also accidentally deleted the signing key I was using before. Oops! Momentary panic until I have the sense to do a search and realize that past me was wise enough to backup the file elsewhere.&lt;/li&gt;
&lt;li&gt;Test that I can create the app bundle successfully.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By this point I more or less had a working Android build, then shifted over to iOS. Since I only &lt;a href=&#34;https://mirror.roytang.net/2018/12/macbook-air-2017-model/&#34;&gt;recently got a Macbook Air&lt;/a&gt;, I actually hadn&amp;rsquo;t tried creating and publishing the iOS build, someone was helping me with that part before. Because of reasons, I wanted/had to do it myself this time around, so a bit of a learning curve and trial-and-error again.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;clone the project from repo, &lt;code&gt;npm install&lt;/code&gt;, try &lt;code&gt;react-native run-ios&lt;/code&gt;, oops missing dependencies&lt;/li&gt;
&lt;li&gt;find out about CocoaPods. Clone a fresh copy of the project, &lt;code&gt;npm install&lt;/code&gt; then &lt;code&gt;pod install&lt;/code&gt; (took a while), then try &lt;code&gt;react-native run-ios&lt;/code&gt; again&lt;/li&gt;
&lt;li&gt;needed to fix some dependencies I was using by manual linking and updating&lt;/li&gt;
&lt;li&gt;camera roll also needed to be added to the podfile (a config I possibly lost when I reset the project)&lt;/li&gt;
&lt;li&gt;fix a bunch of configs before I can actually run and test the app in the simulator&lt;/li&gt;
&lt;li&gt;Success!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, I make my other app changes and test on both my Android phone and the iOS simulator. The changes are easy, so now to try publishing.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Try to publish and upload to iTunesConnect. Takes me a few tries to figure out that I need to be updating stuff like bundle identifier, version number, etc.&lt;/li&gt;
&lt;li&gt;iTunesConnect has some new requirements too (like the iPad version needs to support all orientations) which causes a couple more iterations&lt;/li&gt;
&lt;li&gt;more screenshots are now required when submitting the app!&lt;/li&gt;
&lt;li&gt;I set up TestFlight and verify everything is ok with the published app. (I only tested using the simulator before)&lt;/li&gt;
&lt;li&gt;iOS app now submitted and &amp;ldquo;Waiting for Review&amp;rdquo;. I anticipate they will have some ridiculous request and it might take me a couple more submission rounds before I can publish&lt;/li&gt;
&lt;li&gt;Google Play is more straightforward. &lt;code&gt;gradlew releaseBundle&lt;/code&gt; is already working after my previous effort, so I just upload the bundle and distribute.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I see no indication that I have satisfied Google&amp;rsquo;s action item, but it let me publish the update without complaints, so I&amp;rsquo;m done!&lt;/p&gt;
&lt;p&gt;My initial estimate that the work was nontrivial ends up correct, I spent way more manhours than I expected on the upgrade. Part of it was learning how to do the iOS deployment, so hopefully that ends up easier in the future.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>10X Programmers
</title>
            <link>https://mirror.roytang.net/2019/07/10x-programmers/</link>
            <pubDate>Tue, 16 Jul 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/07/10x-programmers/</guid>
            <description>
            
            &lt;p&gt;The topic of the mythical &lt;a href=&#34;https://softwareengineering.stackexchange.com/questions/179616/a-good-programmer-can-be-as-10x-times-more-productive-than-a-mediocre-one&#34;&gt;&amp;ldquo;10x programmer&amp;rdquo;&lt;/a&gt; has been the topic of discussion recently on tech twitter, due to &lt;a href=&#34;https://twitter.com/skirani/status/1149302828420067328?s=19&#34;&gt;a thread&lt;/a&gt; listing out the supposed signs of being such a mythical beast.&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;10x engineers&lt;br&gt;&lt;br&gt;Founders if you ever come across this rare breed of engineers, grab them. If you have a 10x engineer as part of your first few engineers, you increase the odds of your startup success significantly.&lt;br&gt;&lt;br&gt;OK, here is a tough question.&lt;br&gt;&lt;br&gt;How do you spot a 10x engineer?&lt;/p&gt;&amp;mdash; Shekhar Kirani (@skirani) &lt;a href=&#34;https://twitter.com/skirani/status/1149302828420067328?ref_src=twsrc%5Etfw&#34;&gt;July 11, 2019&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt; 
&lt;p&gt;The thread received a lot of negative responses, mainly because several of these items can be considered red flags indicating someone who doesn&amp;rsquo;t play well with others - hates meetings, prefer irregular hours, poor mentoring ability, disdain for documentation, find process miserable, works alone to produce great code, etc. These attributes remind me of Steve McConnel&amp;rsquo;s description of programming heroics in &lt;em&gt;Professional Software Development&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Combine a shortage of skilled workers with the common tendency to set overly optimistic schedules, and the stage is set for the programming hero. Programming heroes take on challenging assignments and write mountains of code. They work vast amounts of overtime. They become indispensable to their projects. Success, it seems, rests squarely on their shoulders.&lt;/p&gt;
&lt;p&gt;Project managers both love and fear hero programmers because they are smart, temperamental, and sometimes a little self-righteous, and because the managers don&amp;rsquo;t see any way to complete the project without them. In a tight labor market, replacing them isn&amp;rsquo;t an option.&lt;/p&gt;
&lt;p&gt;Unfortunately, the reality is that for every programming hero who is capable of monumental coding achievements, there are other pathological programming disasters who just don&amp;rsquo;t know how to work well with others. They hoard design information and source code. They refuse to participate in technical reviews. They refuse to follow standards established by the team. The sum total of their actions is to prevent other team members from making potentially valuable contributions. A significant number of programming heroes don&amp;rsquo;t turn out to be heroes at all; they turn out to be prima donna programming ball hogs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Despite the negative feedback, I found myself relating to the list in the thread above because most if not all of these items could have been used to describe myself at some point or another in my career. Especially in my earlier years, I had a profound dislike for meetings and documentation and process. I think this sort of attitude stems from the transition from being a solo developer (which you largely are in school) who enjoys the act of coding to being part of a team tasked to deliver the software. The first-time team member may find it difficult to appreciate having to do all these non-coding things (meetings, mentoring, documentation) because he doesn&amp;rsquo;t yet have an appreciation of how they can make things smoother for the entire team.&lt;/p&gt;
&lt;p&gt;Not all of the items in the list are bad habits. Luckily, I think I grew out of most of the bad ones as my career went on and I became part of different teams and filled different roles. Not all of these are bad habits though. Going through the list:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10x engineers hate meetings. They think it is a waste of time and obvious things are being discussed. They attend meetings because the manager has called for a &amp;ldquo;Staff meeting&amp;rdquo; to discuss the features and status.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m still wary of too many unnecessary meetings, but I also understand how getting people away from their IDEs and together for a discussion can be necessary to bring a project back on track.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Timings in the office for 10x engineers is highly irregular. They tend to work when very few folks are around. If there is a crowd or all-hands meeting, they are not visible. Most of them are late-night coders and come late to the office.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think locally, people tend to come in later during the day instead of earlier, due to Metro Manila traffic. I knew that if I wanted some focused time, I could come in early and not much people would bother me. I think this is generally an ok practice, as long as you&amp;rsquo;re still available during core hours when your team needs you.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10x engineers laptop screen background color is typically black (they always change defaults). Their keyboard keys such as i, f, x are usually worn out than of a, s, and e (email senders).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;This one is just straight-up cargo-cult nonsense. Different people have different preferences. (Okay, I do prefer dark background IDEs).&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10x engineers know every line of the code that has gone into production. If a QA or support folks alert an issue, they know precisely where the fault (or bug) is and can fix the same in hours vs days&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have been in this kind of situation, although I guess it&amp;rsquo;s a bit of an exaggeration. Typically if you&amp;rsquo;re working in a team with a nontrivial codebase and you &amp;ldquo;know everything&amp;rdquo;, some of your knowledge may be shallow, and some other devs who spent more time with each module may have more in-depth knowledge of how a particular module works. Having one guy with good oversight into the codebase can be handy for the team - for the reason cited above, they may easily be identify the cause of an issue, but the team should make sure he&amp;rsquo;s not the only guy familiar with everything. Some redundancy is needed so that you aren&amp;rsquo;t always dependent on the same person everytime. It&amp;rsquo;s also bad for you as the developer who knows everything about the codebase, as it can be very hard to go on vacation, you&amp;rsquo;ll have a tendency to get calls such as &amp;ldquo;Hey, just a really quick question, do you know where is the code that does XYZ?&amp;rdquo;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Most of the 10x engineers are full-stack engineers. For them code is code, they don&amp;rsquo;t care whether it is front-end, back-end, API, database, serverless, etc. I have rarely seen them doing UI work.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is still true for me. And just the other day during a call I had to give a disclaimer that my UI work isn&amp;rsquo;t the best lol. I don&amp;rsquo;t think this is indicative of disdain for specialists, there should be room in a team for both &lt;a href=&#34;https://mirror.roytang.net/2016/12/generalists-and-specialists-in-dev-teams/&#34;&gt;generalists and specialists&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10x engineers can convert &amp;ldquo;thought&amp;rdquo; into &amp;ldquo;code&amp;rdquo; in their mind and write it in an iterative fashion. Given a product feature, they can write that entire feature in one or two sittings of 4 to 6 hours with a caffeinated drink without distraction.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I mean, this is just describing a developer who&amp;rsquo;s in the zone. It&amp;rsquo;s true that developers who can get into the zone on a regular basis are likely to be much more productive, that&amp;rsquo;s a tautology.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10x engineers rarely look at help documentation of classes or methods. They know it in memory and can recall from memory. They write code at the same ease as writing English. No breaks, no pauce, just type.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;This one is a bit of BS, especially in modern software development where we have the meme of developers coding by stackoverflow. It might have been true in the older days when there weren&amp;rsquo;t a billion and one APIs and frameworks to know and learn.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10x engineers are always learning new frameworks, languages ahead of everyone in the company. They are not afraid of anything new. If there is something new (e.g. blockchain) they gobble up, setup, experiment before anyone is getting started.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;This one is still true for me as of now. I still enjoy exploring new frameworks, languages and technologies, though I don&amp;rsquo;t always have the time for it. I think this is a good trait for a developer to have, but at the same time it may be dangerous to expect or require this of developers when putting together a team. Not everyone enjoys coding, and developers shouldn&amp;rsquo;t be penalized for not wanting to do software stuff outside of work hours.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10x engineers are poor mentors as they can&amp;rsquo;t teach others on what to do OR parcel the work. They always think &amp;ldquo;It takes too long to teach or discuss with others, I would rather do it myself.&amp;rdquo; They are also poor interviewers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m sure I have uttered some variation of that quote more than once in my career. The tendency to try to carry all the load yourself is not only a disservice to yourself, it&amp;rsquo;s a disservice to your team as you&amp;rsquo;re not helping them grow. I will admit my mentoring skills in the early years wasn&amp;rsquo;t very good - I&amp;rsquo;m the type of mentor who tends to throw you into the deep end so you can quickly learn to swim. This is because that&amp;rsquo;s my personal learning style as well, I learn more from trying stuff than from a lecture or a book. I understand that not everyone learns the same way, so I do actively try to spot opportunities for me to learn to explain things better (I like to think writing in a blog helps!) and for &lt;a href=&#34;https://mirror.roytang.net/2018/10/mentoring-in-software-development/&#34;&gt;better mentoring&lt;/a&gt;. And I think my interviewing skills have improved a bit through the years as well.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10x engineers don&amp;rsquo;t hack things. They write quality code and know exactly how the code has to evolve, and have a mental model of overall code structure. They write at most one design document, and the rest is in the code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The idea of the most productive engineers not hacking things can only come from an idealist who hasn&amp;rsquo;t experienced schedule pressure. While of course quality code is an ideal, the best engineers know when it is necessary to make tradeoffs and workarounds to meet requirements and schedules. And they will write as many documents as needed to make sure things can be understood.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10x engineers rarely job hunt or move out of the company. They move out because you make their life miserable with the process, meetings, training, and other non-value-added activities. If you come across them, hold on to them. Celebrate them.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;This was true for me for the longest time, I stayed with my first company for 15 years before deciding to move on. I would suspect this tends to be true for the type of engineer I described who enjoys the coding part and not so much the dealing with people part of the job. Moving companies means a lot more noncoding work: meeting new people, learning new processes and procedures, more meetings and trainings and interviews and what not. Laziness is probably also a factor.&lt;/p&gt;
&lt;p&gt;The items listed here remind me of the type of programmer stereotype where one programmer goes into his office, tells nobody not to bother him, then comes out after three days having written all the code and solved all the company&amp;rsquo;s problems. It&amp;rsquo;s a ridiculous, dangerous stereotype that doesn&amp;rsquo;t mesh well with how modern software development teams work.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not claiming that 10x engineers don&amp;rsquo;t exist, I&amp;rsquo;m sure they do, and I know from experience many of the above stereotypes can be true. But stereotypes are just that - stereotypes. Every programmer has had a different path and there can be superproductive programmers who also know how to well work with teams and can be great mentors and write great documentation.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also not necessarily claiming to be a 10x programmer even though I claim to relate to the items here (false humility?). I would appreciate being paid a 10x salary though. :D&lt;/p&gt;
&lt;p&gt;A second thread on 10x programmers I like, from Alan Cooper:&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;I’ve worked with more than one “10x” programmer. Their brilliance is astonishing, but their output is rarely practically realized. 1&lt;/p&gt;&amp;mdash; Alan Cooper (@MrAlanCooper) &lt;a href=&#34;https://twitter.com/MrAlanCooper/status/1150215587978596353?ref_src=twsrc%5Etfw&#34;&gt;July 14, 2019&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt; 


            </description>
        </item>
    
        <item>
            <title>Learning from failure
</title>
            <link>https://mirror.roytang.net/2019/04/learning-from-failure/</link>
            <pubDate>Wed, 17 Apr 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/04/learning-from-failure/</guid>
            <description>
            
            &lt;blockquote&gt;
&lt;p&gt;Success is not final, failure is not fatal: it is the courage to continue that counts.&lt;/p&gt;
&lt;p&gt;&amp;ndash; Winston Churchill&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I already typed the above quote into the post, then realized I had already &lt;a href=&#34;https://mirror.roytang.net/2016/07/dealing-with-failure/&#34;&gt;used it before&lt;/a&gt;. Whatever, just goes to show, I&amp;rsquo;m no stranger to failure.&lt;/p&gt;
&lt;p&gt;I was reminded of this quote because recently I prepared a demo for a project that didn&amp;rsquo;t push through. At first I was annoyed at the wasted effort, but I realized that I had wisely taken the demo project as an opportunity to learn/sharpen some skills. Specifically, I used it to study the Django Rest Framework (for the backend API), and a little bit of VueJS (for the frontend), which means that even if the project isn&amp;rsquo;t going anywhere, I was at least able to hone some technical skills. The advantage of always looking to try new things is that your worst case scenario still leads to some self-improvement.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>QUIZ: Well aimed? How well do you know CSS selectors?
</title>
            <link>https://mirror.roytang.net/2019/04/quiz-well-aimed-how-well-do-you-know-css-selectors/</link>
            <pubDate>Sat, 13 Apr 2019 11:07:55 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/04/quiz-well-aimed-how-well-do-you-know-css-selectors/</guid>
            <description>
            Shared via pocket:
    &lt;a href=&#34;https://codepen.io/pehaa/full/ROapJZ&#34;&gt;QUIZ: Well aimed? How well do you know CSS selectors?&lt;/a&gt;

            


            </description>
        </item>
    
        <item>
            <title>A Quick Twitter App I Wrote
</title>
            <link>https://mirror.roytang.net/2019/03/a-quick-twitter-app-i-wrote/</link>
            <pubDate>Mon, 25 Mar 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/03/a-quick-twitter-app-i-wrote/</guid>
            <description>
            
            &lt;p&gt;I wish I had a more concise way to describe it, but I really don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Some time ago this guy I follow on Twitter, &lt;a href=&#34;https://twitter.com/visakanv/&#34;&gt;visakanv&lt;/a&gt; wanted to know how to do a certain search: he wanted to know who a given famous person follows on Twitter, and among those, finds the one who follow him (visakanv), so he could network through them. I might not be explaining the concept too well, here&amp;rsquo;s &lt;a href=&#34;https://twitter.com/visakanv/status/1087316852663939072&#34;&gt;the thread&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, in a fit of &amp;ldquo;I&amp;rsquo;m a bit bored, anything interesting I could do?&amp;rdquo; I figured I could set up a quick webapp to do that. I did a quick PoC in Python/Flask, with &lt;a href=&#34;http://www.tweepy.org/&#34;&gt;Tweepy&lt;/a&gt; for accessing the Twitter API. I normally prefer Django for my webapps, but since this was just a small, quick app, I figured Flask would be ideal. That worked out fine, except that I had to use my personal access tokens to for all the API calls. This means that if many people were using the app, it was likely to hit Twitter&amp;rsquo;s rate limits!&lt;/p&gt;
&lt;p&gt;That was a few months back. A couple of days ago, someone emailed me asking about an error he encountered using the app, and sure enough it was due to the rate limits. I had a couple of free hours last weekend, so I figured I&amp;rsquo;d update the app a bit. I decided to add a Twitter login requirement, that way we can use the individual user&amp;rsquo;s access token instead of using mine globally. This would allow the rate limits to be per user and thus less likely to be hit. Now, I&amp;rsquo;ve tried implementing OAuth login some years ago and it can be terribly complicated, but luckily I found out that there&amp;rsquo;s a nifty Python package called &lt;a href=&#34;https://github.com/singingwolfboy/flask-dance&#34;&gt;Flask-Dance&lt;/a&gt; that made it super easy to add Twitter OAuth to a Flask app. This also let me discard the Tweepy dependency, since the Twitter OAuth session let me query the API endpoints directly. I also did a bit of UI cleanup and error handling and deployed the changes quickly.&lt;/p&gt;
&lt;p&gt;The app is available online at &lt;a href=&#34;https://mirror.roytang.net/apps/twitter&#34;&gt;roytang.net/apps/twitter&lt;/a&gt;. There&amp;rsquo;s a few other Twitter tools I&amp;rsquo;ve been meaning to build, so I&amp;rsquo;ll probably put them here too when I get around to them.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Lies and marketing
</title>
            <link>https://mirror.roytang.net/2019/03/lies-and-marketing/</link>
            <pubDate>Sun, 17 Mar 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/03/lies-and-marketing/</guid>
            <description>
            
            &lt;p&gt;I mentioned before &lt;a href=&#34;https://mirror.roytang.net/2018/10/why-im-not-an-entrepreneur/&#34;&gt;that as an engineer, I&amp;rsquo;m not fond of marketing&lt;/a&gt;.&lt;/p&gt;
&lt;a href=&#34;#81f828351a7fc86eddf3e49ffa420d52-lightbox&#34;&gt;
    &lt;figure&gt;
      &lt;img src=&#34;https://mirror.roytang.net/2019/03/lies-and-marketing/dilbert190312_hu8985074e66017f0ac2c24fec01b6a676_121384_300x0_resize_box.gif&#34; alt=&#34;&#34; title=&#34;&#34; class=&#34;tn&#34; /&gt;
      &lt;figcaption&gt; (Click to view full-size)&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/a&gt;
&lt;div class=&#34;lightbox&#34; id=&#34;81f828351a7fc86eddf3e49ffa420d52-lightbox&#34; style=&#34;display: none;&#34;&gt;
  &lt;a href=&#34;#_&#34;&gt;
    &lt;img src=&#34;https://mirror.roytang.net/2019/03/lies-and-marketing/dilbert190312.gif&#34; /&gt;
  &lt;/a&gt;
  &lt;div class=&#34;lightbox_overlay&#34;&gt;
    &lt;p&gt;&lt;/p&gt;
    &lt;time class=&#34;dt-published&#34; datetime=&#34;17 Mar 2019 5:56am&#34;&gt;17 Mar 2019 5:56am&lt;/time&gt;&lt;a href=&#34;#_&#34;&gt;Close&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Image credit: &lt;a href=&#34;https://dilbert.com/strip/2019-03-12&#34;&gt;Dilbert.com&lt;/a&gt; (Disclaimer: Liking the Dilbert comics is not an endorsement of Scott Adams&amp;rsquo; politics)&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not that I can&amp;rsquo;t be good at salesmanship either. I have a good grasp of communication skills and think I have a decent chance of writing good copy. My main issue is that I&amp;rsquo;ve been exposed many times to sales/marketing practices that just seem dishonest downright or scummy. This happens both with me as a customer and with me on the side of the company trying to do the sales/marketing.&lt;/p&gt;
&lt;p&gt;An example in the software engineering sphere would be a developer writing project proposals in response to tenders, and he is asked to write a technical response confirming that their product supports a particular tender requirement. Except he knows for a fact that he doesn&amp;rsquo;t, and when he raises this concern, he&amp;rsquo;s told not to worry about and that they&amp;rsquo;ll just implement the feature if/when they win the contract. And apparently that&amp;rsquo;s a regular practice in some companies. Disclaimer: I&amp;rsquo;m not saying I have been personally involved in such a situation, but I do know such situations exist.&lt;/p&gt;
&lt;p&gt;I understand the motivation from the sales side, but not only is that intellectually dishonest (to say the least), it also puts your engineering teams in a bind should the project push through - you now committed them to do an extra feature without proper estimation or scheduling and so on.&lt;/p&gt;
&lt;p&gt;For me it&amp;rsquo;s okay to sell something by promoting its strengths and advantages, but not by being dishonest about its capabilities or weaknesses. I suspect that this cripples me a bit in the real world where people with less scruples are more willing to bend the truth to make money. But for me personally, honesty and integrity are more important than making more sales.&lt;/p&gt;



&lt;img src=&#34;https://mirror.roytang.net/2019/03/lies-and-marketing/dilbert190312_hu8985074e66017f0ac2c24fec01b6a676_121384_300x0_resize_box.gif&#34; /&gt;



            </description>
        </item>
    
        <item>
            <title>Don&#39;t stay in the same place too long
</title>
            <link>https://mirror.roytang.net/2019/03/dont-stay-in-the-same-place-too-long/</link>
            <pubDate>Sat, 16 Mar 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/03/dont-stay-in-the-same-place-too-long/</guid>
            <description>
            
            &lt;p&gt;If I could give some advice to someone starting out in their software development career, it would be this: Don&amp;rsquo;t stay in the same place too long. The first company I worked at, I stayed with them for thirteen years, which I now feel was way too long. I have to admit, the work was hard and challenging, but I was young and had a lot of energy and was willing to work the long hours. I was good at the work and the enjoyed the company of the people I worked with so in a certain sense I got comfortable with the status quo and stayed a bit too long. But given a do-over, I would probably prefer to have left after maybe 4-5 years.&lt;/p&gt;
&lt;p&gt;Hopping jobs too quickly can be fantastic for your salary growth, but it has some disadvantages - you don&amp;rsquo;t build close enough relationships, and too many hops doesn&amp;rsquo;t look good on your resume. Four to five years seems like a good period to stay, long enough to learn as much as you can and build your network before moving on.&lt;/p&gt;
&lt;p&gt;The main advantage of not staying too long comes in the form of personal growth. When you stay at a place too long you tend to stagnate. Sure, it&amp;rsquo;s software development so somehow you&amp;rsquo;re always improving, but not improving as quickly as you would be if you were to be thrust into a new environment every few years. Your personal network of like-minded professionals will grow quicker as well when you switch companies. I was thinking about this because I was recently asked if I could refer any senior developers and I realized my personal network of high-level senior technical guys wasn&amp;rsquo;t that deep because of me staying in the same place for so long.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t consider this a &lt;a href=&#34;https://mirror.roytang.net/2018/11/choose-without-regret-and-the-fear-of-missing-out/&#34;&gt;regret&lt;/a&gt;, I&amp;rsquo;m still pretty happy and satisfied with where I am in life. Thinking about this is more of an alternate universe &amp;ldquo;what if&amp;rdquo; scenario. I have to admit that staying that long did have some advantages - I got to work with and meet some good people, younger ones even, who became good friends. But this is not an argument in favor of staying that long, as you would still have the chance to build relationships even if you move on. It&amp;rsquo;s possible in that alternate universe I may have made even better friends or may have even been friends with the same ones I have now somehow.&lt;/p&gt;
&lt;p&gt;So, given a chance to give advice to my younger self, I would probably recommend the second path. There is more uncertainty there to be sure, but life is risk, and to live is to explore. Taking the change to move to a new environment optimizes for personal growth, and it&amp;rsquo;s probably better to be doing it when you are young and full of energy.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>What are the pros and cons of a programming career?
</title>
            <link>https://mirror.roytang.net/2019/03/what-are-the-pros-and-cons-of-a-programming-career/</link>
            <pubDate>Tue, 12 Mar 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/03/what-are-the-pros-and-cons-of-a-programming-career/</guid>
            <description>
            
            &lt;p&gt;Another repost from my Quora answers, this time some info for anyone looking to move into programming.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What are the pros and cons of making your career in programming?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is a very rewarding career financially. Software development often ranks in the top 10 highest-earning careers in most countries&lt;/li&gt;
&lt;li&gt;There is a lot of scope - you could be developing web applications, mobile applications, embedded applications, client-side, server-side, data analysis, artificial intelligence, games, etc&lt;/li&gt;
&lt;li&gt;It is very difficult to be bored. You can always automate away the boring stuff. Different projects always present different challenges. The field is evolving rapidly so there are always new things to learn.&lt;/li&gt;
&lt;li&gt;It is both a creative endeavor and a technical endeavor&lt;/li&gt;
&lt;li&gt;Lots of opportunities for remote work/working from home, if that is your thing&lt;/li&gt;
&lt;li&gt;You get to work with a lot of smart people&lt;/li&gt;
&lt;li&gt;Access to a worldwide community of people who think in a similar manner. Most beginner and intermediate problems easily solved through the internet&lt;/li&gt;
&lt;li&gt;Generally, working in a programming company is more likely to be a meritocracy - people with better technical skill are more likely to be valued&lt;/li&gt;
&lt;li&gt;There are a lot of opportunities - if you are unhappy where you are, you can easily look for another opportunity&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is not for everybody. It requires a certain type of thinking that you may or may not be used to. Typically the field needs the ability to think logically and incrementally (step-by-step through how things happen) and to understand how multiple components work and interact together. More advanced challenges will also require significant out of the box thinking&lt;/li&gt;
&lt;li&gt;After some time, you will tend to interpret things more literally. This is a side effect of working with computer programs that may affect your social interactions&lt;/li&gt;
&lt;li&gt;Many people outside of the field will often find it difficult to understand the amount of work that goes into a computer program. This can lead to difficult conversations, especially with clients&lt;/li&gt;
&lt;li&gt;Your nonprogrammer friends and relatives will often ask you for technical help, even though you know nothing about their printer and will most likely just google the information&lt;/li&gt;
&lt;li&gt;Typically, the industry is very bad at estimation and scheduling, leading to a lot of overtime and the corresponding stress&lt;/li&gt;
&lt;li&gt;The field is evolving rapidly, so there is often a need for a career programmer to self-educate to remain up-to-date with current trends&lt;/li&gt;
&lt;li&gt;Encountering a highly difficult problem that no one else has been able to solve may drive you insane&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.quora.com/What-are-the-pros-and-cons-of-making-your-career-in-programming/answer/Roy-Tang&#34;&gt;Link to the original answer on Quora&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(Annoyingly I just realized that the permalink to a Quora answer may change if the text of the answer changes, since they use the question slug instead of an id in the url.)&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Studying a large project codebase
</title>
            <link>https://mirror.roytang.net/2019/03/studying-a-large-project-codebase/</link>
            <pubDate>Sun, 03 Mar 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/03/studying-a-large-project-codebase/</guid>
            <description>
            
            &lt;p&gt;Given my &lt;a href=&#34;https://mirror.roytang.net/2018/12/quora/&#34;&gt;recent misgivings about Quora&lt;/a&gt;, I thought it might be a good idea to cross-post some of my answers from there into this blog, with some edits even. So here&amp;rsquo;s the first one! (stuff in &lt;em&gt;italics&lt;/em&gt; were added during the cross-post)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How can you read and study a large software project source code?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Attacking a large, existing codebase that you are unfamiliar with can be a daunting endeavor. Don&amp;rsquo;t expect that you will be able to easily navigate the codebase quickly after just a few days of studying it. Familiarity will come with experience.&lt;/p&gt;
&lt;p&gt;Some things that can help:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&amp;rsquo;t try to understand everything all at once. Figure out what is interesting to you and focus your efforts there&lt;/li&gt;
&lt;li&gt;Follow the path of data. Easiest way to see how the program works. For example, if I were studying a web application that processes request parameters to generate a search result, I would trace how the program captures the parameters from the requests object, formulates it into a search query, sends it to the database, receives the results, formats the results, then sends it back to the browser. Looking at the typical paths through which data traverses the system can give you a good overview of where everything is. &lt;em&gt;this may be a problem if you&amp;rsquo;re not familiar with the technology or framework the codebase is using, as some of the details may have too many layers of abstraction to trace. In that case, better to brush up and have a basic understanding of the tech first.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Try to fix a bug or implement some sort of change. If it&amp;rsquo;s an open source project, this is probably the best way to familiarize yourself with the codebase &lt;em&gt;and&lt;/em&gt; get more involved at the same time. I&amp;rsquo;d suggest starting with minor UI bugs, those tend to be easiest to figure out since a text search of the source will often point you to where the correction needs to be made. For example, if the bug is that a particular error message is being shown under the wrong conditions, you can use a text search to find where the error message is declared, then trace all the code points that generate that message, figure out which one you need to update (probably by trial and error) and fix accordingly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.quora.com/How-can-you-read-and-study-a-large-software-project-source-code/answer/Roy-Tang&#34;&gt;Link to the original answer on Quora&lt;/a&gt;&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Reddit PH: Software Dev Q&amp;A
</title>
            <link>https://mirror.roytang.net/2019/02/reddit-ph-software-dev-qa/</link>
            <pubDate>Wed, 20 Feb 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/02/reddit-ph-software-dev-qa/</guid>
            <description>
            
            &lt;p&gt;I had some free time the other day so I randomly decided to post in the &lt;a href=&#34;https://www.reddit.com/r/Philippines/&#34;&gt;PH subreddit&lt;/a&gt;&amp;rsquo;s regular afternoon &lt;a href=&#34;https://www.reddit.com/r/Philippines/comments/arsxjg/afternoon_random_discussion_feb_18_2019/&#34;&gt;random discussion thread&lt;/a&gt;, asking for &lt;a href=&#34;https://www.reddit.com/r/Philippines/comments/arsxjg/afternoon_random_discussion_feb_18_2019/egpog87/&#34;&gt;questions about software development&lt;/a&gt;. I ended up typing some longish answers, I thought I&amp;rsquo;d copy them over to the blog in case anyone was interested. TBH I meant more like StackOverflow type questions with specific technical problems, but I ended up answering mostly career-related questions, which is fine, but disclaimer: I don&amp;rsquo;t claim to be an expert, these are just my opinions on things.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://www.reddit.com/r/Philippines/comments/arsxjg/afternoon_random_discussion_feb_18_2019/egpolbt/&#34;&gt;capybara_c0de&lt;/a&gt;: project/s junior devs should include in his/her portfolio?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;ve technical interviewed a lot of entry-level/junior level devs. Honestly any kind of project would probably be okay, as long as you&amp;rsquo;re able to explain what role you played, bonus points if you&amp;rsquo;re able to discuss technical decisions you made (why was this choice made, and why not this other one etc) or specific technical challenges you were able to overcome. That kind of stuff. Really, if you can demonstrate that you really worked on a project (and not just coasted by on teammates help etc), you&amp;rsquo;re already an above average interview.&lt;/p&gt;
&lt;p&gt;Entry-level candidates might have more trouble since they often won&amp;rsquo;t have a portfolio, but the quality of candidates is a bit low locally, so if you can demonstrably do simple stuff like loops, recursion, stacks, etc you&amp;rsquo;re probably already an above average interview. Bigger companies will have written exams to filter out peeps who can&amp;rsquo;t do that though.&lt;/p&gt;
&lt;p&gt;More random technical interviewing tips, even though you didn&amp;rsquo;t ask for that: being able to communicate well is a big plus, mandatory even. Lack of confidence makes the interviewer worry that you don&amp;rsquo;t really know what you&amp;rsquo;re talking about. Sometimes I&amp;rsquo;ll slip in something I know to be wrong &amp;ldquo;Diba pag cinall mo yan, ganito mangyayari?&amp;rdquo;, and see how the candidate reacts&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://www.reddit.com/r/Philippines/comments/arsxjg/afternoon_random_discussion_feb_18_2019/egpp2yr/&#34;&gt;veeequalseyeare&lt;/a&gt;: How many years of experience do you have? And what&amp;rsquo;s your basic salary?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why do people keep asking about salary? That&amp;rsquo;s not really a programming or software dev question. I am a freelancing consultant at the moment, so I can&amp;rsquo;t really give a simple answer to that anyway (it&amp;rsquo;s almost always &amp;ldquo;it depends&amp;rdquo;)&lt;/p&gt;
&lt;p&gt;I will answer the experience one - 15ish years in the industry (yeah, I&amp;rsquo;m old).&lt;/p&gt;
&lt;p&gt;Edit: Also, there is a high range in the salaries of software devs in this country (not sure if the same elsewhere, probably), because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;some people are better at selling themselves than others&lt;/li&gt;
&lt;li&gt;some devs are way better than others (and thus are more valuable)&lt;/li&gt;
&lt;li&gt;each dev&amp;rsquo;s career path and growth is often unique, so it&amp;rsquo;s difficult to compare, especially once you rack up some years of experience&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;i.e. for a given number of years of experience, the salary band will be quite wide.&lt;/p&gt;
&lt;p&gt;I get that when you&amp;rsquo;re starting out, salary is a big deal, but honestly if you&amp;rsquo;re a decent enough programmer, you can get into any place with above average salary. You can grow your salary a bit faster by hopping jobs every so often, but too many hops looks bad on a resume.&lt;/p&gt;
&lt;p&gt;Anyway, my advice is that once you get to a decent salary range that is good for you (i.e. you&amp;rsquo;re not starving), focus less on comparing yourself to other people, and more on increasing your own value and managing your work/life balance. Money isn&amp;rsquo;t everything, beyond basic survival.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://www.reddit.com/r/Philippines/comments/arsxjg/afternoon_random_discussion_feb_18_2019/egpqfdb/&#34;&gt;getmeoutofherealive&lt;/a&gt;: Anong advice ang maibibigay mo kapag gusto magcareer shift? Ex: IT Support to Development/Programming.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Shifting careers from IT support isn&amp;rsquo;t an unreasonable proposition, but you need to already know your stuff before going in for interviews. You will also still need some basic level of programming knowledge. A lot of things you&amp;rsquo;ll need can be learned on the fly/on the job (like usage of specific languages or tools or libraries), but you need a good basic foundation to build on.&lt;/p&gt;
&lt;p&gt;If I&amp;rsquo;m interviewing someone coming from a nonprogrammming background, I&amp;rsquo;ll basically be treating her as an entry-level candidate. You will need to be able to justify why I should hire you over say, a fresh grad who still remembers his java programming from his college days. The interviewer will need to know you&amp;rsquo;re not trying to BS your way into the position. Having some work you can show off (side projects or whatever) can be a big help. You can also cite anything you&amp;rsquo;re doing in your current domain that might show off some related skillset. Like, if you did some minor scripting to gather support statistics, something like that? IDK if IT support people do that kind of stuff. See my other interview tips in the other answer elsewhere in this thread too.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://www.reddit.com/r/Philippines/comments/arsxjg/afternoon_random_discussion_feb_18_2019/egpquyz/&#34;&gt;captainph&lt;/a&gt;: web services in web development, ELI5 please&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wait, what do you mean? Web services are like web pages, except they&amp;rsquo;re endpoints that usually do only one thing and return some machine readable format like JSON or XML instead of HTML that can be rendered and viewed by a human in a browser. Basically you send them an HTTP request and they maybe do some processing and spit back an HTTP response that you consume.&lt;/p&gt;
&lt;p&gt;Some types are simpler than others - REST-based web services are usually very simple for example, and can sometimes be invoked directly via the browser address bar. Some follow more complicated protocols (SOAP) that support things like authentication and signing and so on.&lt;/p&gt;
&lt;p&gt;Web services are developed the same way as normal web pages, with the usual backend processing and stuff (but there are some frameworks to make this easier, especially for complicated protocols like SOAP), but the client program (the one calling the web services) does not have to be another web application. It can be a mobile app for example.&lt;/p&gt;
&lt;p&gt;One of the typical usages would be third-party integration. For example, Philhealth I believe currently has some kind of e-Claims web services in development (maybe? IDK the current status), where the idea is to allow third-party systems to submit Philhealth claims electronically. The idea is that individual hospitals have their own medical records systems, those systems can send HTTP requests to the e-claims web services in order to for example check whether a patient is eligible for a claim.&lt;/p&gt;
&lt;p&gt;Ok, sorry I know that explanation doesn&amp;rsquo;t really work for a 5-year old lol&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://www.reddit.com/r/Philippines/comments/arsxjg/afternoon_random_discussion_feb_18_2019/egptmll/&#34;&gt;acequeen21&lt;/a&gt;: Do you have plans going abroad? Either work temporarily or move permanently. Why or why not?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nah, I like living here, in spite of everything, and I&amp;rsquo;m programming avails me a good living naman. I&amp;rsquo;ve worked abroad on short stints (pinadala ng company), but TBH, I had never even considered leaving the country long-term, but I started looking into it after 2016. Hassle for me na ma-uproot and to try to start over elsewhere.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Hacktoberfest
</title>
            <link>https://mirror.roytang.net/2019/02/hacktoberfest/</link>
            <pubDate>Thu, 14 Feb 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/02/hacktoberfest/</guid>
            <description>
            
            &lt;p&gt;Last October I participated in #Hacktoberfest, &lt;a href=&#34;https://hacktoberfest.digitalocean.com/&#34;&gt;sponsored by DigitalOcean and Github&lt;/a&gt;. It&amp;rsquo;s a &amp;ldquo;celebration&amp;rdquo; to promote open source activity, and basically you just need to submit 5 pull requests to any github repository, and they give away swag to anyone who completes the activity. Microsoft held a [counterpart celebration] where they only require you to submit 1 pull request to any Microsoft repository.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve always wanted to start participating in Open Source, but it&amp;rsquo;s a bit difficult to find a good place to contribute (other than logging issues of course). So when I heard about this activity, I thought &amp;ldquo;Why not?&amp;rdquo; Anyway, I&amp;rsquo;m not ashamed to admit 4 out of my 5 PRs were to small/minor projects, some of them seemingly created specifically to help farm Hacktoberfest PRs lol. I did manage to get &lt;a href=&#34;https://github.com/Microsoft/vscode/pull/61206&#34;&gt;one pull request accepted&lt;/a&gt; for &lt;a href=&#34;https://mirror.roytang.net/2018/11/my-history-in-text-editors/&#34;&gt;my current favorite editor&lt;/a&gt;, VSCode! So that&amp;rsquo;s something! It was just a super minor bug fix, but it feels good to know that some future build of VS Code may have some code I wrote in it. This also qualified me to get swag from Microsoft! I haven&amp;rsquo;t gotten any open source-related swag since the dearly-departed &lt;a href=&#34;https://mirror.roytang.net/2008/11/free-flex-shirt-from-adobe/&#34;&gt;Adobe Flex shirt from 2008&lt;/a&gt; (RIP Adobe Flex).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m only posting about this now, four months after the fact, because my Hacktoberfest swag only came in last week and I had to swing by the post office to pick them up. Here&amp;rsquo;s some pics:&lt;/p&gt;








    
    
        
    
    
    
        
            
            
            
            
              
            
            
            &lt;a href=&#34;#1abc78fd66250c45e478e05a73ee6563-lightbox&#34;&gt;
                &lt;figure&gt;
                  &lt;img src=&#34;https://mirror.roytang.net/2019/02/btxn5aybmto/1c680980eabb587ef1039ca69885ac8f_hu4350d9514153e1db5174154929a7c9dc_91815_300x0_resize_q75_box.jpg&#34; alt=&#34;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft!&#34; title=&#34;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft!&#34; class=&#34;tn&#34; /&gt;
                  &lt;figcaption&gt;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft! (Click to view full-size)&lt;/figcaption&gt;
                &lt;/figure&gt;
            &lt;/a&gt;
            &lt;div  class=&#34;lightbox&#34; id=&#34;1abc78fd66250c45e478e05a73ee6563-lightbox&#34; style=&#34;display: none;&#34;&gt;
              &lt;a href=&#34;#_&#34;&gt;
                &lt;img src=&#34;https://mirror.roytang.net/2019/02/btxn5aybmto/1c680980eabb587ef1039ca69885ac8f.jpg&#34; /&gt;
              &lt;/a&gt;
              &lt;div class=&#34;lightbox_overlay&#34;&gt;
                &lt;p&gt;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft!&lt;/p&gt;
                &lt;time class=&#34;dt-published&#34; datetime=&#34;14 Feb 2019 5:56am&#34;&gt;14 Feb 2019 5:56am&lt;/time&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/02/btxn5aybmto/&#34;&gt;View post&lt;/a&gt;&lt;a href=&#34;#_&#34;&gt;Close&lt;/a&gt;
              &lt;/div&gt;
            &lt;/div&gt;            
        
            
            
            
            
              
            
            
            &lt;a href=&#34;#b3baecfb685ea3cdb683c2a70ca82fdb-lightbox&#34;&gt;
                &lt;figure&gt;
                  &lt;img src=&#34;https://mirror.roytang.net/2019/02/btxn5aybmto/398c1e8e920c14aea211821f7894aaee_huf6fd3fc2169926707e7e410b9e8680d7_101173_300x0_resize_q75_box.jpg&#34; alt=&#34;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft!&#34; title=&#34;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft!&#34; class=&#34;tn&#34; /&gt;
                  &lt;figcaption&gt;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft! (Click to view full-size)&lt;/figcaption&gt;
                &lt;/figure&gt;
            &lt;/a&gt;
            &lt;div  class=&#34;lightbox&#34; id=&#34;b3baecfb685ea3cdb683c2a70ca82fdb-lightbox&#34; style=&#34;display: none;&#34;&gt;
              &lt;a href=&#34;#_&#34;&gt;
                &lt;img src=&#34;https://mirror.roytang.net/2019/02/btxn5aybmto/398c1e8e920c14aea211821f7894aaee.jpg&#34; /&gt;
              &lt;/a&gt;
              &lt;div class=&#34;lightbox_overlay&#34;&gt;
                &lt;p&gt;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft!&lt;/p&gt;
                &lt;time class=&#34;dt-published&#34; datetime=&#34;14 Feb 2019 5:56am&#34;&gt;14 Feb 2019 5:56am&lt;/time&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/02/btxn5aybmto/&#34;&gt;View post&lt;/a&gt;&lt;a href=&#34;#_&#34;&gt;Close&lt;/a&gt;
              &lt;/div&gt;
            &lt;/div&gt;            
        
            
            
            
            
              
            
            
            &lt;a href=&#34;#32abe86d3845a6d6595e617d27d7eb2a-lightbox&#34;&gt;
                &lt;figure&gt;
                  &lt;img src=&#34;https://mirror.roytang.net/2019/02/btxn5aybmto/b90e15c7e12736caa6de77ae1240e00f_hue534623f8e2a3d3c2e1c4d7fd367f905_164525_300x0_resize_q75_box.jpg&#34; alt=&#34;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft!&#34; title=&#34;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft!&#34; class=&#34;tn&#34; /&gt;
                  &lt;figcaption&gt;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft! (Click to view full-size)&lt;/figcaption&gt;
                &lt;/figure&gt;
            &lt;/a&gt;
            &lt;div  class=&#34;lightbox&#34; id=&#34;32abe86d3845a6d6595e617d27d7eb2a-lightbox&#34; style=&#34;display: none;&#34;&gt;
              &lt;a href=&#34;#_&#34;&gt;
                &lt;img src=&#34;https://mirror.roytang.net/2019/02/btxn5aybmto/b90e15c7e12736caa6de77ae1240e00f.jpg&#34; /&gt;
              &lt;/a&gt;
              &lt;div class=&#34;lightbox_overlay&#34;&gt;
                &lt;p&gt;Passed by the post office today to pick up some parcels, turns out it was the #hacktoberfest swag I got for participating last October. Thanks Digital Ocean/Github/Microsoft!&lt;/p&gt;
                &lt;time class=&#34;dt-published&#34; datetime=&#34;14 Feb 2019 5:56am&#34;&gt;14 Feb 2019 5:56am&lt;/time&gt;&lt;a href=&#34;https://mirror.roytang.net/2019/02/btxn5aybmto/&#34;&gt;View post&lt;/a&gt;&lt;a href=&#34;#_&#34;&gt;Close&lt;/a&gt;
              &lt;/div&gt;
            &lt;/div&gt;            
        
    

&lt;p&gt;I&amp;rsquo;m not really a stickers guy, so I&amp;rsquo;m not sure where I&amp;rsquo;ll put the stickers. Maybe they&amp;rsquo;d look good on my Macbook Air? I like the shirts though, since Americans have better shirt sizes from me than locally.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m still not &amp;ldquo;active&amp;rdquo; really on open source, but at least it was a nice first step. Maybe next October I&amp;rsquo;ll look for some bigger PRs to do!&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>TriviaStorm: Text and Answer parsing
</title>
            <link>https://mirror.roytang.net/2019/02/triviastorm-text-and-answer-parsing/</link>
            <pubDate>Sun, 03 Feb 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/02/triviastorm-text-and-answer-parsing/</guid>
            <description>
            
            &lt;p&gt;A while back I started a &lt;a href=&#34;https://mirror.roytang.net/2017/02/weekend-project-twitter-trivia-bot/&#34;&gt;Twitter trivia bot as a weekend project&lt;/a&gt;. That bot is still &lt;a href=&#34;https://twitter.com/triviastorm&#34;&gt;up and running on Twitter&lt;/a&gt;, you can check it out there!&lt;/p&gt;
&lt;p&gt;But today, I thought I&amp;rsquo;d write about the answer-checking mechanism used by the bot. It was a bit interesting to me because it was the first nontrivial use I had for &lt;a href=&#34;https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/unit-tests/&#34;&gt;Django&amp;rsquo;s unit testing framework&lt;/a&gt;. I&amp;rsquo;m not too keen on unit testing web functionality (something I still have to learn), but this seemed an appropriate first use of a unit test framework for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the bot had to be able to handle a wide variety of answers&lt;/li&gt;
&lt;li&gt;there were a lot of test cases to check and a single checking function handling everything - I couldn&amp;rsquo;t risk breaking previous working tests&lt;/li&gt;
&lt;li&gt;inputs were discrete and outputs were easily checkable&lt;/li&gt;
&lt;li&gt;I needed to be able to add new test scenarios all the time as more problematic answers were provided&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The project currently isn&amp;rsquo;t open source, but I did make a gist of the &lt;code&gt;tests.py&lt;/code&gt; I used &lt;a href=&#34;https://gist.github.com/roytang/9199962097bdf3ca2aa8ec9c43bd7ef8&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;check&lt;/code&gt; function basically accepts three parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a checking mode (currently only supports EXACT and ALL_ANYORDER)&lt;/li&gt;
&lt;li&gt;the answer phrase provided by the player&lt;/li&gt;
&lt;li&gt;a set of valid answers accepted for the question&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&amp;rsquo;s a number of test cases already handled:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;checking should be case-insensitive&lt;/li&gt;
&lt;li&gt;articles should be ignored if they&amp;rsquo;re at the start of the answer phrase&lt;/li&gt;
&lt;li&gt;numbers should be acceptable for the spelled-out versions i.e. &amp;ldquo;7&amp;rdquo; should be accepted for &amp;ldquo;seven&amp;rdquo;, and vice versa&lt;/li&gt;
&lt;li&gt;some minor soundex (phonetic matching) support (via &lt;a href=&#34;https://github.com/jamesturk/jellyfish&#34;&gt;Python Jellyfish&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;handling of questions that support multiple answers. This is what ALL_ANYORDER is for - it means that all the given answers must be provided, but they can be in any order. i.e. if the valid answer set is &lt;code&gt;&amp;quot;Huey&amp;quot;&lt;/code&gt;, &lt;code&gt;&amp;quot;Dewey&amp;quot;&lt;/code&gt; and &lt;code&gt;&amp;quot;Louie&amp;quot;&lt;/code&gt;, then &lt;code&gt;&amp;quot;Louie Huey Dewey&amp;quot;&lt;/code&gt; should be accepted as an answer&lt;/li&gt;
&lt;li&gt;nonalphanumeric characters should be treated as whitespace, except in some special cases&lt;/li&gt;
&lt;li&gt;special case: abbreviations like &lt;code&gt;&amp;quot;don&#39;t&amp;quot;&lt;/code&gt; or &lt;code&gt;&amp;quot;can&#39;t&amp;quot;&lt;/code&gt; should be treated as if they were a single term like &lt;code&gt;&amp;quot;dont&amp;quot;&lt;/code&gt; or &lt;code&gt;&amp;quot;cant&amp;quot;&lt;/code&gt; instead of &lt;code&gt;&amp;quot;don t&amp;quot;&lt;/code&gt; or &lt;code&gt;&amp;quot;can t&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The answer checking definitely still isn&amp;rsquo;t perfect, but I&amp;rsquo;m pretty happy with where it&amp;rsquo;s at right now. There is also definitely an element of subjectivity as to which answers should be accepted. One time a player complained that his answer &lt;code&gt;&amp;quot;Batman vs Superman Dawn of Justice&amp;quot;&lt;/code&gt; should count for &lt;code&gt;&amp;quot;Batman v Superman: Dawn of Justice&amp;quot;&lt;/code&gt;, but for this particular question I had chosen not to allow &amp;ldquo;vs&amp;rdquo; for &amp;ldquo;v&amp;rdquo; because that was the actual movie title, which might be unreasonable now that I think about it!&lt;/p&gt;
&lt;p&gt;I do know that I need to implement a better &amp;ldquo;synonym&amp;rdquo; handling, i.e. mapping of &lt;code&gt;&amp;quot;v&amp;quot;&amp;lt;-&amp;gt;&amp;quot;vs&amp;quot;&lt;/code&gt; and other terms like &lt;code&gt;&amp;quot;mr&amp;quot;&amp;lt;-&amp;gt;&amp;quot;mister&amp;quot;&lt;/code&gt; or &lt;code&gt;&amp;quot;natl&amp;quot;&amp;lt;-&amp;gt;&amp;quot;national&amp;quot;&lt;/code&gt;. The problem with handling things like that is that is the possible combinations of phrases expands when multiple such terms are found in the same answer, so it can&amp;rsquo;t scale too well. I suppose I need to normalize the answer sets at the time the question is defined. What do you know, I figured out how to do something just by writing a blog post!&lt;/p&gt;
&lt;p&gt;I do have a bunch of other enhancements planned for the trivia bot, including support for slack and discord, and a longer time frame roadmap, but I&amp;rsquo;m not sure when I can commit more time to it. Still, it&amp;rsquo;s turned out to be a pretty fun endeavor, I&amp;rsquo;m hoping it leads to something cool!&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>I still Google the most basic things
</title>
            <link>https://mirror.roytang.net/2019/01/i-still-google-the-most-basic-things/</link>
            <pubDate>Thu, 17 Jan 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/01/i-still-google-the-most-basic-things/</guid>
            <description>
            
            &lt;p&gt;I&amp;rsquo;ve been working with Javascript for more than a decade. Last week while helping another developer debug a problem, I had to Google how to check if an element exists in a Javascript array, something superbasic, that one would expect most newbies to know. I&amp;rsquo;m sure I Google some superbasic thing at least once a week. It&amp;rsquo;s not embarassing or anything, it&amp;rsquo;s a common occurrence. I&amp;rsquo;m surely not alone. Just last night a tweet about this crossed my TL:&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;In C++ we don&amp;#39;t say &amp;quot;Missing asterisk&amp;quot; we say &amp;quot;error C2664: &amp;#39;void std::vector&amp;lt;block,std::allocator&amp;lt;_Ty&amp;gt;&amp;gt;::push_back(const block &amp;amp;)&amp;#39;: cannot convert argument 1 from &amp;#39;std::_Vector_iterator&amp;lt;std::_Vector_val&amp;lt;std::_Simple_types&amp;lt;block&amp;gt;&amp;gt;&amp;gt;&amp;#39; to &amp;#39;block &amp;amp;&amp;amp;&amp;#39;&amp;quot; and i think that&amp;#39;s beautiful&lt;/p&gt;&amp;mdash; mcc (@mcclure111) &lt;a href=&#34;https://twitter.com/mcclure111/status/1002648636516282368?ref_src=twsrc%5Etfw&#34;&gt;June 1, 2018&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt; 
&lt;p&gt;People joke about how developers wouldn&amp;rsquo;t know what to do if Stackoverflow wasn&amp;rsquo;t around, but it&amp;rsquo;s a fact of modern software development that most developers will have access to a ton of easily searchable reference material available online. That&amp;rsquo;s why things like phone interviews where people ask you to explain what the &lt;code&gt;volatile&lt;/code&gt; keyword means in order to test whether you&amp;rsquo;re really a Java expert are BS. (I have a Java certification and while I&amp;rsquo;m sure I knew the answer to that at some point, if I needed it today, it would certainly come in the form of a search result.).&lt;/p&gt;
&lt;p&gt;Software development is a difficult field, and it can sometimes be intimidating to work with more senior developers or ask them questions or show them your code. But it gets easier when you realize not everyone knows everything all the time, and that not even the most senior people know everything. And that this ability to seek out advice and yes, even stackoverflow results, is part of the skillset you need to be developing.&lt;/p&gt;
&lt;p&gt;The nature of the field is that we are almost always encountering new situations and new problems and new challenges (well, either that or be bored doing something irrelevant). That means a lot of the time when we are working on something worthwhile, we are figuring out things as we go.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Finding Time to Learn new Things
</title>
            <link>https://mirror.roytang.net/2019/01/finding-time-to-learn-new-things/</link>
            <pubDate>Mon, 14 Jan 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/01/finding-time-to-learn-new-things/</guid>
            <description>
            
            &lt;p&gt;Someone responded to my post on &lt;a href=&#34;https://mirror.roytang.net/2019/01/tech-to-learn-in-2019/&#34;&gt;things to learn in 2019&lt;/a&gt; by asking how one finds the inspiration to learn all of the things. Well, my first answer was that those are just things I find interesting and may look into, but that&amp;rsquo;s not really an answer for the inspiration part.&lt;/p&gt;
&lt;p&gt;Software development is a very wide field, one where the amount of things you can learn increases daily, so it&amp;rsquo;s almost impossible to keep up with everything. I think that having a natural tendency or curiosity towards learning new things is a distinct advantage in this industry. Having at least some passing familiarity with a wide range of technologies gives you more options in both job hunting and deciding on technical approaches.&lt;/p&gt;
&lt;p&gt;When interviewing developers, I often viewed it as a distinct advantage when a candidate invested their own time in trying out new tech. However, I realize that spending your own time to learn things on your own is another form of privilege that I have. Not all developers will have the free time or motivation to do that unless it was part of their work tasks. So insisting that developers should do this in their free time is a bit elitist to say the least.&lt;/p&gt;
&lt;p&gt;I read &lt;a href=&#34;http://coding-is-like-cooking.info/2018/12/who-should-pay-for-upskilling-software-developers-individuals-or-employers/&#34;&gt;a blog post&lt;/a&gt; the other day saying it&amp;rsquo;s actually beneficial for companies to pay for their employees to learn stuff during work time. Quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Giving developers time to learn makes good business sense in a competitive world where attracting and retaining skilled people is an advantage in the marketplace.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While this makes sense, the problem with this is that from experience, many companies are running their projects on very tight schedules, some often almost always behind schedule, which makes giving developers free time for self-improvement will often be unattractive to higher-ups. I know that in my previous jobs, the only way I could justify learning a new technology would be if it was required or important for a specific upcoming project. In a way, having the space to allow developers this privilege is in itself a privilege that not all companies can afford.&lt;/p&gt;
&lt;p&gt;Supposedly in the modern softeare industry, demand for developers is still high compared to supply so individuals should still have some leverage here and should vote with our feet. Younger or entry-level developers should ask companies about their policies for upskilling their employees and prefer those which give them more opportunities, and disfavor companies where the devs&amp;rsquo; technical knowledge tends to stagnate, so that companies will learn to adjust. Such factors can only be good for their career in the long run.&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Git vs CVS
</title>
            <link>https://mirror.roytang.net/2019/01/git-vs-cvs/</link>
            <pubDate>Sat, 12 Jan 2019 02:06:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/01/git-vs-cvs/</guid>
            <description>
            
            &lt;p&gt;A while back we were tasked with helping a client&amp;rsquo;s internal dev team to migrate their repositories from Subversion to Git. The distributed VCS seemed ideal for their situation - they had a very small in-house dev team managing contributions from external subcontractors. The main rationale was that their process of merging contributions from the external developers was extremely complicated and often resulted in conflicts that were challenging to merge. Before this, I hadn&amp;rsquo;t actually used Git too deeply myself (aside from cloning stuff from Github), and especially not in a team setting, so the training one of our other engineers gave them was a good opportunity for me to become familiar with Git as well. During the succeeding months we guided them through the first few pull requests and merges; their repository structure and build process was quite complicated so it took us a while to iron out the kinks. There was often some difficulty when merging PRs from old forks and so on. I mostly let our other engineer who had more Git experience handle any merging. Sometimes I wondered about whether the transition was really worth it for the client, but it all seemed to end up okay.&lt;/p&gt;
&lt;p&gt;I was reminded of this because I recently tried the whole workflow of fork and change code and pull request and merge with upstream to resolve conflicts in a larger, non-trivial project. (I don&amp;rsquo;t count the few PRs I did for Hacktoberfest last year, as those were relatively simple). This one was a whole new submodule that I had to merge into the main repository, so there were a lot of changes on my end. I wasn&amp;rsquo;t expecting any conflicts though, because I was only adding new stuff and not modifying existing code. So I tried to make my PR without merging from upstream, not expecting any issues. Unfortunately, it turns out some CR/LF shenanigans between my Windows machine and the repo meant one of my commits had more &amp;ldquo;lines changed&amp;rdquo; than I intended, resulting in a merge conflict. Fortunately, it was easy to simply merge from upstream and resolve the offending files manually. Didn&amp;rsquo;t take me more than 5 minutes to fix the issue.&lt;/p&gt;
&lt;p&gt;My experience with this was a bit amazing in retrospect. Mostly because for a large part of my career to as recently as four years ago, I worked with CVS as a version control system, which is ancient by modern standards. I remember during my first project, we had three branches: DEV, UAT and PROD. And moving changes from any given branch to another was a pain, each file had to be manually re-applied to the other branch. So my instinct has always been to dread any large-scale merge (something I have to unlearn apparently).&lt;/p&gt;
&lt;p&gt;Git has a few other advantages of course. One of my favorites is being able to address specific commits individually by hash, and see the file changes and so on. When we were using CVS if you wanted to rollback to a specific change, you had to have the foresight to have tagged that change so that you can check it out again. And CVS for us was always very slow for larger projects with thousands of files, leading to annoyingly long build cycles and wait times. Git seems to be a lot more performant.&lt;/p&gt;
&lt;p&gt;I hold no ill will towards my old company for making me use CVS all those years. When I started out it was a reasonable choice, but as time went by better options simply became available, and like many companies it was difficult to leave entrenched systems. I did propose the use of distributed VCS over the years; at first Mercurial and later Git as well, but there was never a good time to migrate without being overly disruptive (this happens when Everything Is Urgent). Probably if I had gotten in on a larger project on the ground floor, but the last time I had a chance for that was around 2007 I think, when Git was still in infancy. Still, I heard from people still there that they&amp;rsquo;re already in migrating to Git, so good for them (even though they waited til I was gone!).&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Tech to Learn in 2019
</title>
            <link>https://mirror.roytang.net/2019/01/tech-to-learn-in-2019/</link>
            <pubDate>Tue, 08 Jan 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/01/tech-to-learn-in-2019/</guid>
            <description>
            
            &lt;p&gt;One of the things about self-identifying as a &amp;ldquo;Full Stack Developer&amp;rdquo; or &lt;a href=&#34;https://mirror.roytang.net/2019/01/solution-architect/&#34;&gt;&amp;ldquo;Solution Architect&amp;rdquo;&lt;/a&gt; is that there&amp;rsquo;s no shortage of things to learn, and oftentimes it&amp;rsquo;s good for your career-wise to at least have some passing knowledge of a bunch of technologies. It helps that I really like the field as well. I try to make sure I study or learn at least one new programming language or framework every year (though I am willing to stretch that definition as needed). Here&amp;rsquo;s a list of tech I&amp;rsquo;m looking at learning in 2019:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Rust or Golang.&lt;/strong&gt; Between Python and JS, the last few years for me have been mostly focused on dynamically-typed languages that don&amp;rsquo;t require precompilation. With my recent &lt;a href=&#34;https://mirror.roytang.net/2019/01/revisiting-c---after-a-decade/&#34;&gt;revisit of C++&lt;/a&gt;, I&amp;rsquo;m a bit interested in checking out a new statically-typed compiled language. Rust and Golang or the primary candidates here, I&amp;rsquo;ll probably toy around with both to see which one I like better. The problem here is that I typically do web-based programs, so finding a project where it would be worth using one of these might be challenging.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Swift&lt;/strong&gt;. Since I recently bought a &lt;a href=&#34;https://mirror.roytang.net/2018/12/macbook-air-2017-model/&#34;&gt;Macbook Air&lt;/a&gt;, that opens up the possibility of trying out Swift. I was involved in a mobile project when Swift came out and found it interesting when I was first going through the documentation - maybe because I thought Objective-C was a hot mess (or at least our codebase was back then). I never did get a chance to try it out, so maybe I will this year - if I find an appropriate mobile app to work on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kotlin&lt;/strong&gt; is to Java and the JVM as Swift is to Objective-C and iOS. It&amp;rsquo;s young, but as a JVM language it has direct interoperability with Java programs, and it&amp;rsquo;s supposedly a first-class language nowadays for Android development. If I do decide to work on a native mobile app, Kotlin is a prime choice for the Android version. (Assuming I&amp;rsquo;m willing to dive back into the JVM ecosystem&amp;hellip;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSS Grid&lt;/strong&gt;. I&amp;rsquo;ll be the first to admin I&amp;rsquo;m a bit behind on frontend practices; my default layout framework is still &lt;a href=&#34;https://getbootstrap.com/&#34;&gt;Twitter&amp;rsquo;s Bootstrap&lt;/a&gt;. If I get the chance, I&amp;rsquo;ll do some of my personal web projects using CSS grid.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vue.js&lt;/strong&gt;. Speaking of frontends, I never got too deep into the current crop of frontend frameworks. Funny story, we were meeting with a database vendor to discuss solution options for a client and I mentioned I had already built a small prototype web application using their product. They asked me what framework I was using on the frontend and when I told them I wasn&amp;rsquo;t using any, I was told that was &amp;ldquo;hardcore&amp;rdquo;. While I do think it&amp;rsquo;s helpful for full-stack web developers to be familiar with vanilla JS, I am interested in trying out at least one of the three prevalent frontend frameworks - Vue, React, and Angular. I have kind of tried React a bit with React Native, but at this moment, I&amp;rsquo;m leaning more towards Vue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Machine Learning&lt;/strong&gt;. Specifically, I want to try out some text generation using recurrent neural networks, like those posts where people generate screenplays etc. I&amp;rsquo;m not too conversant with the theory, but reading some examples and tutorials I think this is something doable. Just need a reasonable corpus of texts to work with&amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TDD&lt;/strong&gt;. Yup, test-driven development. I&amp;rsquo;m way behind the curve on this one. I understand the theory and the benefits, but I&amp;rsquo;ve never been in the position to have TDD in my projects. It&amp;rsquo;s difficult sometimes to get management buy-in. At my past job, all of the testing was still manual. I&amp;rsquo;ve dabbled in TDD a bit, tried out some frameworks, but never full-scale in one of my projects. Maybe I&amp;rsquo;ll try to write some tests for one of my side projects this year.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s it. I think it&amp;rsquo;s quite a lot actually. I&amp;rsquo;ll review this post towards the end of the year to see which of these I end up doing. Or maybe I&amp;rsquo;ll get into a freelance project that involves something new not on this list, who knows?&lt;/p&gt;
&lt;p&gt;What about you, what&amp;rsquo;s your list for 2019?&lt;/p&gt;



            </description>
        </item>
    
        <item>
            <title>Solution Architect
</title>
            <link>https://mirror.roytang.net/2019/01/solution-architect/</link>
            <pubDate>Mon, 07 Jan 2019 05:56:56 +0000</pubDate>
            <author>hello@roytang.net (Roy Tang)</author>
            <guid>https://mirror.roytang.net/2019/01/solution-architect/</guid>
            <description>
            
            &lt;p&gt;Although I still primarily identify as a &amp;ldquo;Full Stack Developer&amp;rdquo;, during the past few years I&amp;rsquo;ve also found myself in a role called &amp;ldquo;Solution Architect&amp;rdquo;. The thing about being a solution architect is that there isn&amp;rsquo;t really a clear definition of the role, what it involves, or the scope of responsibility. I suppose it depends largely on the organization and the project. The role mostly involves making techical decisions on a larger scale, like project-wide or organization-wide, rather than on the micro day-to-day technical decisions involved in a typical software development involves. These decisions can encompass not just coding, but also requirements gathering, application design, database design, modelling, testing methodology, processes, deployment, and so on.&lt;/p&gt;
&lt;p&gt;This is actually pretty much what I was doing during my last full-time job anyway. I was basically involved in technical decision-making at all levels. So there&amp;rsquo;s not much change, except that now there&amp;rsquo;s a formal name/title to the role.&lt;/p&gt;
&lt;p&gt;Despite that role, in my past job I still insisted on taking on some of the coding work myself, typically the more difficult ones. I think it&amp;rsquo;s important as a technical person to maintain some familiarity with the low-level coding details, even when you&amp;rsquo;re transitioning to higher-level work like management or architectural work. (Also, I still enjoy the vagaries of day-to-day coding.)&lt;/p&gt;
&lt;p&gt;For a solution architect in particular, it helps to be familiar with a wide array of technologies, since it informs your technical recommendations. You won&amp;rsquo;t always get such exposure through official projects either, so it helps to be interested in these things on your own so you can read up or try them out in your spare time.&lt;/p&gt;
&lt;p&gt;Interestingly, the term &amp;ldquo;solution architect&amp;rdquo; has been increasing in usage over the past few years, if Google Trends is any indication:&lt;/p&gt;
&lt;a href=&#34;#8538e8aa0341cd3eb7a1630f595cac40-lightbox&#34;&gt;
    &lt;figure&gt;
      &lt;img src=&#34;https://mirror.roytang.net/2019/01/solution-architect/solution-architect_hua2140a621f54624710810700693deb41_39199_300x0_resize_box_2.png&#34; alt=&#34;&#34; title=&#34;&#34; class=&#34;tn&#34; /&gt;
      &lt;figcaption&gt; (Click to view full-size)&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/a&gt;
&lt;div class=&#34;lightbox&#34; id=&#34;8538e8aa0341cd3eb7a1630f595cac40-lightbox&#34; style=&#34;display: none;&#34;&gt;
  &lt;a href=&#34;#_&#34;&gt;
    &lt;img src=&#34;https://mirror.roytang.net/2019/01/solution-architect/solution-architect.png&#34; /&gt;
  &lt;/a&gt;
  &lt;div class=&#34;lightbox_overlay&#34;&gt;
    &lt;p&gt;&lt;/p&gt;
    &lt;time class=&#34;dt-published&#34; datetime=&#34;7 Jan 2019 5:56am&#34;&gt;7 Jan 2019 5:56am&lt;/time&gt;&lt;a href=&#34;#_&#34;&gt;Close&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(I originally tried Google Trend&amp;rsquo;s embed option, but the embedded chart kept crashing my browser tab, so now you get a screen capture instead)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d asked me five years ago I would have said such a role was within the scope of a senior engineer or technical lead. I guess as time goes on, the role becomes more distinct and formalized? At this point, I think the concept might still be a bit too nebulous for anyone to really specialize in it though.&lt;/p&gt;



&lt;img src=&#34;https://mirror.roytang.net/2019/01/solution-architect/solution-architect_hua2140a621f54624710810700693deb41_39199_300x0_resize_box_2.png&#34; /&gt;



            </description>
        </item>
    
    </channel>
  </rss>