<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id></id>
  <title>Tyler Trout's Blog</title>
  <updated>2024-05-02T08:32:21-04:00</updated>
  <subtitle>Full Stack Web Development</subtitle>
  <icon>/media/favicon.png</icon>
  <logo>/media/favicon.png</logo>
  <link rel="alternate" type="text/html" href="" />
  <link rel="self" type="application/atom+xml" href="/atom.xml" />
  <generator uri="https://github.com/FuGangqiang/mdblog.rs">mdblog.rs</generator>
  <entry>
    <id>/posts/Keeping_Learners_Consistent.html</id>
    <title>Keeping Learners Consistent</title>
    <updated>2024-02-11T10:28:18-05:00</updated>
    <published>2024-02-11T10:28:18-05:00</published>
    <link href="/posts/Keeping_Learners_Consistent.html"/>
    <summary>Consistency is key to user retention. A language learning app should encourage consistent use of the app. This should be done in ways that will truly engage users to take action. A reminder email is worthless if it does not remind the user of why they are learning a language to begin with. Using churn surveys is an effective way to determine why users leave your app so that you can work on keeping more of them.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Keeping Learners Consistent</h1>
<p>The most efficient way to learn a new language is to be consistent. A user who consistently uses your language learning app is more likely to become a loyal customer. User retention is a KPI (Key Performance Indicator) that is important to track when gauging the success of a language learning app. A consistent learner is more likely to learn the language and convert into a paying customer.</p>
<h2>Use a streak system</h2>
<p>A streak system is a great way to encourage consistent use of your app. It is a simple way to reward users for using your app every day and is good for showing users their progress. Few things are as motivating as pre-existing momentum.</p>
<p>It isn't enough to merely track consecutive days in the app. The user must also be reminded of their streak, especially if the user has yet to renew it for the day. A notification or other clever reminder should capture the attention of a user and motivate them to keep up their consistency. Duolingo for iPhone features an optional widget that gradually dims throughout the day. This creates a sense of urgency for the user to complete their daily goal.</p>
<h2>Allow learners to encourage each other</h2>
<p>Learning a language is much more difficult if you do not have someone with whom to speak it with. An app should encourage users to engage with one another. This keeps learners motivated and is a natural way to grow your app's audience.</p>
<p>Duolingo features a &quot;Friends Quest&quot; feature, in which users complete a challenge together. This is another substantial way to inspire users to use the app consistently. Learners are subtly motivated to compete, as the quest displays each user's contributions, usually as lessons completed.</p>
<h2>Send reminders</h2>
<p>Sending reminders is a great way to get users to use your app as well. Users should have the option to opt out of reminders, however, there should be a prompt to select the level of notifications to receive when first installing the app or creating an account.</p>
<p>When sending reminders, it is important to not come across as annoying or &quot;spammy&quot;. The reminders should be sent at a time that is convenient for the user. The reminders should also be sent in a way that engages the user to take action. Each user is different, so the contents of an email must be personalized to the learner. Invoking guilt might be effective for some learners, but for others, it can be highly discouraging.</p>
<h2>Use a churn survey</h2>
<p>Knowing why users are leaving your app is essential to understanding how to increase user retention. A churn survey is a great way to gather this information. This is presented to users as they are leaving the app. Churn surveys could be given after removing an account or installing an app.</p>
<p>Frustrated users will be eager to share their pain points while using your app. These clear responses can lead to concrete actions that will improve user retention. This is preferable to users simply leaving, and not having a reason as to why.</p>
<h2>Conclusion</h2>
<p>Consistency is key to user retention. A language learning app should encourage consistent use of the app. This should be done in ways that will <strong>truly</strong> engage users to take action. A reminder email is worthless if it does not remind the user of why they are learning a language to begin with. Using churn surveys is an effective way to determine why users leave your app so that you can work on keeping more of them.</p>
<p>To learn more about language-learning, check out our blog specifically on the topic: <a href="https://blog.lingualearn.net">https://blog.lingualearn.net</a>.</p>

        ]]>
    </content>
  </entry><entry>
    <id>/posts/Building_A_Language_App_For_The_Web.html</id>
    <title>Building A Language App For The Web</title>
    <updated>2024-01-07T13:50:29-05:00</updated>
    <published>2024-01-07T13:50:29-05:00</published>
    <link href="/posts/Building_A_Language_App_For_The_Web.html"/>
    <summary>Building a language learning app for the web is a great way to reach a wide audience. Here are some things to keep in mind while developing a language-learning app on the web.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Building A Language App For The Web</h1>
<h2>A Growing E-Learning Market</h2>
<p>Did you know that the e-learning market is expected to reach $325 billion by 2025? That’s a $72 billion increase from 2017. The e-learning industry is booming, and it’s not going to slow down anytime soon. Here are some things to keep in mind while developing a language-learning app on the web.</p>
<h2>Support browser translation</h2>
<p>Some users may not be English speakers. It’s important to support browser translation so that users can translate the app into their native language. But on a language learning app, we certainly do not want all of the content to be translated. The content of specific elements should stay in the language it is supposed to represent. That is why you must use the <code>translate=&quot;no&quot;</code> attribute and <code>&quot;notranslate&quot;</code> class on elements that should retain their original content.</p>
<p>If your app should validate user input (like in the form of a challenge), be sure to pass the correct answer into a hidden element. That way when the browser translates from English to a different language, the correct answer will be translated to the user's preferred language as well. This value must be formatted so that validation can be handled properly, more on this in the next section.</p>
<h2>Keep challenges lenient</h2>
<p>When learning a new language, it’s important to keep the challenges lenient. You don't want a beginner to be discouraged by failing to include an <a href="https://www.npmjs.com/package/remove-accents">accent mark</a> or for entering a common misspelling. If your language app is going to include interactive typing challenges, be sure to format the user's input, and validate it fairly.</p>
<p>Include a list of multiple correct inputs, and format the user's input to the simplest possible form. This means converting to lowercase, trimming white-space, and removing accents or special characters. The application should then validate this, starting with the simplest solution.</p>
<h2>Define your translations</h2>
<p>In contrast to browser translations, you could also define your translations. This is a great way to ensure that the content of your app is translated correctly. Frameworks like SilverStripe may support modules like <a href="https://github.com/tractorcow-farm/silverstripe-fluent">Fluent</a>, in which translated content can be defined manually. The end user would then visit a sub-URL for each translated language, e.g. (example.com/es for the Spanish version of the app).</p>
<p>Taking this a step further, you may decide to use a translation API like <a href="https://cloud.google.com/translate/docs/">Google Translate</a>. This would allow you to translate content on the fly, and to support a wider range of languages. However, this would also require you to pay for the API and implement a caching strategy to avoid exceeding your quota. The <a href="https://crates.io/crates/text-translator">text-translator Rust Crate</a> allows you to do this programmatically.</p>
<h2>Conclusion</h2>
<p>To learn more about language-learning, check out our blog specifically on the topic: <a href="https://blog.lingualearn.net">https://blog.lingualearn.net</a>.</p>

        ]]>
    </content>
  </entry><entry>
    <id>/posts/Developing_a_language_learning_app_in_2024.html</id>
    <title>Developing A Language Learning App In 2024</title>
    <updated>2023-12-09T18:19:34-05:00</updated>
    <published>2023-12-09T18:19:34-05:00</published>
    <link href="/posts/Developing_a_language_learning_app_in_2024.html"/>
    <summary>Why You Should Build a Language Learning App in 2024. At least 30 million people started learning a new language after the Covid-19 pandemic. The number of people learning a new language is expected to grow by 20% in 2024. Here are some ways you can build a great language-learning app in 2024.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Developing a Language Learning App</h1>
<p>At least 30 million people started learning a new language after the Covid-19 pandemic. The number of people learning a new language is expected to grow by 20% in 2024. Here are some ways you can build a great language-learning app in 2024.</p>
<h2>Use characters</h2>
<p>Using different characters can add cultural authenticity to a language-learning experience. In a traditional classroom setting, the only people available to converse with are fellow students and the instructor. This severely limits the types of interactions that one can experience with the language. Some languages, like Korean, use specific honorifics that depend on the social hierarchy of individuals involved in a conversation. For example, a student would not use the imperative or casual forms with a teacher, unless they were performing some type of role-playing activity.</p>
<p>Your app should include multiple characters, each with their type of relationship with the learner. This allows for a more diverse set of possible interactions. Learners might be exposed to a specific type of interaction, which they would never get the chance to experience in a typical classroom. Without this exposure, learners might have difficulty grasping the appropriate use of these social dynamic rules.</p>
<h2>Encourage the use of Pop Culture</h2>
<p>Pop culture is one of the top reasons for enrollment in language learning programs. Knowing how to use Pop Culture in a language learning program can help you attract more students and make them feel more connected to the language.</p>
<p>Learners who develop their interests in the context of the second language are more likely to continue engaging with the culture after a learning session. Your app should be sprinkled with little nods to the memes and culture of the language your learners are studying.</p>
<h2>Use AI to simulate real-life conversations</h2>
<p>In 2023, AI tools like ChatGPT have made significant strides in adoption and popularity. While impressive, there is still much work to be done for AI to be used as an effective language-learning tool. 2024 could be the year that AI chatbots are used to simulate human conversations at a discourse level.</p>
<p>Using a chatbot to simulate a real-life conversation can help students feel more relaxed and confident when speaking in a foreign language. This can help students overcome their fear of speaking a foreign language in front of native speakers. Your app should include some form of an AI-powered chatbot. This will allow new learners to become more comfortable with the new language before having to use it in front of another human being.</p>
<h2>Focus on comprehensibility over accuracy</h2>
<p>Comprehensibility is the ability to be understood by others, which is more important than abiding by the written rules of the language. However, comprehensibility as a subject is difficult to define and make lesson plans out of. It is a topic that includes multiple sub-categories, such as pronunciation, grammar, and even idiomatic slang.</p>
<p>Pronunciation training in technology is still in its infancy. However, some promising technologies can help students improve their pronunciation. Computer Aided Pronunciation Training (CAPT), can help students become more comprehensible, in a stress-free manner. Many students may be afraid to speak a second language, as they are afraid of embarrassment in front of their peers. CAPT, however, would allow students to practice their pronunciation in a way in which they are not self-conscious of their growing ability.</p>
<p>Your app should include some form of CAPT. This will allow students to practice their pronunciation in a stress-free environment. This will help them become more comprehensible in the long run. This could be through evaluated recordings or guided audio snippets.</p>
<h2>Conclusion</h2>
<p>Language learning is a growing industry. The number of people learning a new language has surged in recent years. This trend is expected to continue into 2024. There are many ways you can build a great language-learning app in 2024. You can use characters to add cultural authenticity to your app. You can encourage the use of Pop Culture in your app. You can use AI to simulate real-life conversations. You can focus on comprehensibility over accuracy. These are just some of the ways you can build a great language-learning app in 2024.</p>
<p>To learn more about language-learning, check out our blog specifically on the topic: <a href="https://blog.lingualearn.net">https://blog.lingualearn.net</a>.</p>

        ]]>
    </content>
  </entry><entry>
    <id>/posts/rebuilding_my_blog.html</id>
    <title>Rebuilding My Blog</title>
    <updated>2023-10-03T08:36:50-04:00</updated>
    <published>2023-10-03T08:36:50-04:00</published>
    <link href="/posts/rebuilding_my_blog.html"/>
    <summary>WordPress is a great platform to get started blogging with, but for me, it's more than what I really need. I don't need a database, I don't need a web server, and I don't need a CMS. At the end of the day, all I really need is static HTML, CSS, and JavaScript.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Rebuilding a WordPress blog with rust</h1>
<p>WordPress is a great platform to get started blogging with, but for me, it's more than what I really need. I don't need a database, I don't need a web server, and I don't need a CMS. At the end of the day, all I really need is static HTML, CSS, and JavaScript.</p>
<h2>Save some money</h2>
<p>Premium Web hosting with Hostinger costs about $90 a year. That's money I could be saving by switching to free hosting with GitHub Pages.</p>
<h2>What about content management?</h2>
<p>I'm a developer, so I'm comfortable with writing markdown. I decided to use a static site generator to convert my markdown files into HTML. I chose mdblog, a cargo crate I found on <a href="https://crates.io">crates.io</a>. This crate, however, had lacking support for Windows environments (still my daily driver), so I was required to fork it and make updates myself. This mostly involved adding checks for carriage returns in post files.</p>
<h2>Importing posts</h2>
<p>I had to write a script to convert my WordPress posts into markdown files. I exported my posts from WordPress as an XML file. Then I wrote a script to parse the XML file and convert the posts into markdown files. I used the crate &quot;html2md&quot; to convert the HTML content from my posts into the markdown format, after which the script outputs markdown files for each post. I then use those markdown files to generate HTML files from the mdblog templates. The journey of my posts starts at the HTML embedded in the WordPress XML export file, to markdown files, back to formatted HTML files.</p>
<h2>Isn't that more work than necessary?</h2>
<p>It is, but it doesn't come without benefits. I can now write my posts in markdown, and I can use git to track changes to my blog. My local text editor is a highly customized Neovim, which makes writing and editing posts much faster and more intuitive to me. This also comes with the added benefit of AI assisted writing, via GitHub Copilot.</p>
<h2>Outcome</h2>
<p>Overall, I'm happy with how the new blog turned out. I did, unfortunately, have to sacrifice some features, such as the ability to edit with the WordPress mobile app and the ability for users to leave comments, but I think the trade-off was worth it (most comments were spam anyway). My hosting is now free, I do not need to worry about spam comment, and editing the blog posts is now easier than ever.</p>

        ]]>
    </content>
  </entry><entry>
    <id>/posts/Manage_multiple_versions_with_gitflow_and_GitHub.html</id>
    <title>Manage Multiple Versions With Gitflow And GitHub</title>
    <updated>2022-09-04T21:46:01+00:00</updated>
    <published>2022-09-04T21:46:01+00:00</published>
    <link href="/posts/Manage_multiple_versions_with_gitflow_and_GitHub.html"/>
    <summary>Imagine this: you want to add a new feature to a repository, but it involves a change that will break backward compatibility. You could release a new major version, but other repositories may depend on the previous version of your software.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Manage multiple versions with GitFlow and GitHub</h1>
<p>Imagine this: you want to add a new feature to a repository, but it involves a change that will break backward compatibility. You could release a new major version, but other repositories may depend on the previous version of your software.</p>
<p>In this case, you need to support an older version simultaneously with the next major version. Git-flow is a branching strategy that eases the burden of managing multiple versions, but it can be tricky to implement in a team using GitHub. Git-flow provides several benefits:</p>
<ul>
<li>Git-flow uses distinct and obvious branch roles: branches are annotated with their specific purpose (feature, release, support, and hotfix).</li>
<li>Git-flow keeps consistent histories: patches made for older versions are automatically merged back into the development branch.</li>
<li>Git-flow uses a release branching strategy that ensures a higher quality of released software, and lower stress for the developer managing branches, as release branches are deleted as soon as they are merged and tagged.</li>
</ul>
<p>These benefits can be achieved in a GitHub setting, but it requires some manual automation with GitHub actions.</p>
<h2>&quot;feature/feature_name&quot; into &quot;develop&quot;</h2>
<p>All new features should be created from the &quot;develop&quot; branch. No GitHub actions need to run on pull requests created for feature branches into the develop branch, but you may choose to enforce labels on your features. Making sure the feature in review has the appropriate semantic versioning label will be helpful for when we create &quot;release&quot; branches later.</p>
<p>When a pull request is finished for a feature branch, the PR can be squashed and merged into the &quot;develop&quot; branch. Squashing should <strong>not</strong> occur for release or hotfix PRs for reasons that will be mentioned.</p>
<h2>Draft a release on &quot;release/*&quot; push</h2>
<p>By far the best benefit of using git-flow with GitHub is that releases can be properly reviewed in the form of a pull request. A GitHub action could be created that drafts a release when a push is made to a &quot;release/*&quot; branch.</p>
<pre><code>on:
  push:
    branches:
      - release/**
      - hotfix/**

permissions:
  contents: read

jobs:
  update_release_draft:
    permissions: write-all
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@master
        with:
          fetch-depth: 0

      # NEW RELEASES
      # Check if this is a release branch
      - name: Check For release branch
        id: is_release_branch
        continue-on-error: true
        run: |
          FILTEREDBRANCHNAME=$(git branch | grep &quot;\* release&quot;)
          echo &quot;filtered_branch_name=$FILTEREDBRANCHNAME&quot; &gt;&gt; $GITHUB_OUTPUT

      # Get the release tag
      - name: Get release Tag
        id: get_release_tag
        if: steps.is_release_branch.outputs.filtered_branch_name != ''
        run: |
          RELEASETAG=$(git branch | grep \* | sed -re &quot;s/(\* )?release\///;s/(\w+)\/.*/\1/g&quot;)
          echo &quot;release_tag=$RELEASETAG&quot; &gt;&gt; $GITHUB_OUTPUT

      # Draft Release with release branch
      - name: Draft Release with release branch
        id: update_release_draft_with_release_branch
        if: steps.is_release_branch.outputs.filtered_branch_name != ''
        uses: tiller1010/release-drafter@master
        with:
          tag: ${{ steps.get_release_tag.outputs.release_tag }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
</code></pre>
<h2>Publish the draft release when &quot;release/*&quot; PRs are merged</h2>
<p>Before merging a release PR, the changes should be reviewed again by another developer. If any additional commits are made, these should <strong>not</strong> be squashed before merging. This way, the other branches are aware of the additional commits, and merges can occur more smoothly, bringing the fixes along too.</p>
<p>When a release branch is merged into &quot;main&quot; the release that was drafted for that pull request can be published. Doing so creates a tag. After the corresponding release is published, a changelog PR is automatically created, approved, and merged. Them, the &quot;release/*&quot; branch can be automatically deleted by a GitHub action.</p>
<pre><code>on:
  pull_request:
    types:
      - closed
    branches:
      - main
      - support/*

jobs:
  publish_release:
    if: ${{ github.event.pull_request.merged }}
    runs-on: ubuntu-latest
    steps:

      - name: Checkout
        uses: actions/checkout@master
        with:
          fetch-depth: 0

      - name: Get repository name
        id: repo-name
        uses: tiller1010/get-repo-name-action@master
        with:
          with-owner: 'true'

      - name: Get release id
        id: get_release_id
        run: |
          TOKEN=&quot;${{ secrets.GITHUB_TOKEN }}&quot;
          REPO=&quot;${{ steps.repo-name.outputs.repository-name }}&quot;
          RELEASEID=$(curl -H &quot;Accept: application/vnd.github+json&quot; -H &quot;Authorization: token $TOKEN&quot; https://api.github.com/repos/$REPO/releases)
          RELEASEID=$(echo &quot;$RELEASEID&quot; | grep \&quot;id\&quot;  | head -n 1 | sed -re &quot;s/[a-z]*//g;s/[-|,|:|'\&quot;]//g;s/\s//g&quot;)
          echo &quot;release_id=$RELEASEID&quot; &gt;&gt; $GITHUB_OUTPUT
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Payload info
        id: payload_info
        uses: tiller1010/payload-info-action@master
        continue-on-error: true

      # START HOTFIX RELEASE RE-DRAFT
      # END HOTFIX RELEASE RE-DRAFT

      - name: Publish release
        uses: eregon/publish-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          release_id: ${{ steps.get_release_id.outputs.release_id }}


      # START CHANGELOG PORTION
      - name: Check if releasing for a support branch
        id: is_support_branch
        continue-on-error: true
        run: |
          FILTEREDBRANCHNAME=$(git branch | grep &quot;\* support&quot;)
          echo &quot;filtered_branch_name=$FILTEREDBRANCHNAME&quot; &gt;&gt; $GITHUB_OUTPUT

      - name: Generate Changelog
        id: changelog
        if: steps.is_support_branch.outputs.filtered_branch_name == ''
        uses: tiller1010/tag-changelog@main
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          exclude_types: other,doc,chore
          config_file: .github/tag-changelog-config.js

      - name: Output Changelog
        id: output_changelog
        if: steps.is_support_branch.outputs.filtered_branch_name == ''
        run: TAGCONTENT=&quot;${{ steps.changelog.outputs.changelog }}&quot;;CHANGELOG=$(cat CHANGELOG.md);CHANGELOG=$(echo &quot;$CHANGELOG&quot; | sed -e &quot;s/# Changelog//&quot;);echo -e &quot;# Changelog\n\n$TAGCONTENT$CHANGELOG&quot; &gt; CHANGELOG.md

      - name: Create Pull Request
        id: create_pr
        if: steps.is_support_branch.outputs.filtered_branch_name == ''
        uses: peter-evans/create-pull-request@v4
        with:
          title: Auto-Update CHANGELOG.md
          commit-message: Updated CHANGELOG.md
          labels: automerge

      - name: Approve PR
        if: steps.is_support_branch.outputs.filtered_branch_name == ''
        uses: hmarr/auto-approve-action@v3
        with:
          review-message: Auto approved automated PR
          pull-request-number: ${{ steps.create_pr.outputs.pull-request-number }}
          github-token: ${{ secrets.SOME_USERS_PAT }}

      - name: Auto merge
        if: steps.is_support_branch.outputs.filtered_branch_name == ''
        uses: pascalgn/automerge-action@main
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PULL_REQUEST: ${{ steps.create_pr.outputs.pull-request-number }}
          MERGE_DELETE_BRANCH: true

      - name: Post Changelog Checkout
        if: steps.is_support_branch.outputs.filtered_branch_name == ''
        uses: actions/checkout@master
        with:
          ref: main
      # END CHANGELOG PORTION

      # START MERGE INTO DEVELOP
      # END MERGE INTO DEVELOP

      # Remove the release or hotfix branch after publishing
      - name: Remove PR branch
        uses: dawidd6/action-delete-branch@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branches: ${{ steps.payload_info.outputs.branch }}
</code></pre>
<h2>Merge main into develop</h2>
<p>After a release branch is merged into the main branch, it should be merged back into the &quot;develop&quot; branch.</p>
<pre><code>...
      # START MERGE INTO DEVELOP
      # Merge support changes into develop so they can be included in the next release
      - name: Merge support -&gt; develop
        if: steps.is_support_branch.outputs.filtered_branch_name != ''
        uses: devmasx/merge-branch@master
        with:
          type: now
          target_branch: develop
          github_token: ${{ secrets.GITHUB_TOKEN }}
          message: Merged support into develop

      # Merge main changes into develop
      - name: Merge main -&gt; develop
        if: steps.is_support_branch.outputs.filtered_branch_name == ''
        uses: devmasx/merge-branch@master
        with:
          type: now
          from_branch: main
          target_branch: develop
          github_token: ${{ secrets.GITHUB_TOKEN }}
          message: Merged main into develop
      # END MERGE INTO DEVELOP
...
</code></pre>
<h2>Re-Draft releases on &quot;hotfix/*&quot; push</h2>
<p>When you must support a previous minor or major version, you should start by checking out that tag. Make sure to checkout the latest patch for that minor or major version. Then, you should create a &quot;support/*.x&quot; branch.</p>
<p>Support branches should function similarly to the &quot;main&quot; branch. Tags will be created when &quot;hotfix/*&quot; branches are merged into it, and commits should <strong>not</strong> be added to it directly.</p>
<p>After creating the support branch, a &quot;hotfix/*&quot; branch with the next patch number should be created. These are similar to the &quot;release/*&quot; branches, as they should be reviewed in a pull request, and when they are merged.</p>
<p>Again, <strong>do not</strong> squash commits for hotfix branches before merging. That way, when support branch commits are merged back into the develop and main branches, the same commit SHAs appear, and the changes are shown as merged.</p>
<p>Git actions should <strong>publish</strong> the drafted release and delete the &quot;hotfix/*&quot; branch (similar to when a &quot;release/*&quot; branch is merged, see the above example).</p>
<pre><code>...
      # START HOTFIX RELEASE RE-DRAFT
      - name: Check if releasing a hotfix
        id: is_hotfix_branch
        continue-on-error: true
        run: |
          PRBRANCH=&quot;${{ steps.payload_info.outputs.branch }}&quot;
          FILTEREDBRANCHNAME=$(echo &quot;$PRBRANCH&quot; | grep &quot;hotfix&quot;)
          echo &quot;filtered_pr_branch_name=$FILTEREDBRANCHNAME&quot; &gt;&gt; $GITHUB_OUTPUT

      - name: Get hotfix Tag from branch name
        id: get_hotfix_tag
        if: steps.is_hotfix_branch.outputs.filtered_pr_branch_name != ''
        run: |
          HOTFIXBRANCH=&quot;${{ steps.payload_info.outputs.branch }}&quot;
          RELEASETAG=$(echo &quot;$HOTFIXBRANCH&quot; | sed -re &quot;s/(\* )?hotfix\///;s/(\w+)\/.*/\1/g&quot;)
          echo &quot;release_tag=$RELEASETAG&quot; &gt;&gt; $GITHUB_OUTPUT

      - name: Get Last Tag created on this branch
        id: last_tag
        if: steps.is_hotfix_branch.outputs.filtered_pr_branch_name != ''
        run: |
          LASTTAG=$(git describe --tags | sed -re &quot;s/-.+//&quot;)
          echo &quot;last_tag_on_branch=$LASTTAG&quot; &gt;&gt; $GITHUB_OUTPUT

      # Re-Draft Release with hotfix tag
      # &quot;release-drafter&quot; works by checking the changes of merged pull requests.
      # For support or main branch hotfixes, there is only one merged hotfix PR, which is only now available,
      # so we need to re-draft the release with the recently merged PR for release notes.
      - name: Draft Release with hotfix tag
        id: update_release_draft_with_hotfix_branch
        if: steps.is_hotfix_branch.outputs.filtered_pr_branch_name != ''
        uses: tiller1010/release-drafter@master
        with:
          tag: ${{ steps.get_hotfix_tag.outputs.release_tag }}
          last_tag: ${{ steps.last_tag.outputs.last_tag_on_branch }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      # END HOTFIX RELEASE RE-DRAFT
...
</code></pre>
<h2>Merge support into main and develop</h2>
<p>If a bug existed in a previous version, it is likely that it still exists in the latest version too. Similar to &quot;release/*&quot; branches that are merged into the main branch, support branches too should be merged into the main branch.</p>
<p>This happens by first creating a feature PR to merge a cloned support branch into the develop branch. There is, however, a higher potential for this to produce merge conflicts. In that case, a developer must resolve the conflict on their own, and push the resolved merge into the feature branch, set to merge into develop.</p>
<p>Now that the support branch is merged into develop, it can be merged into the main branch. This should be done in another release pull request.</p>
<p>I hope you decide to use git-flow in your projects! It adds much-needed sanity to the management of multiple versions of software.</p>

        ]]>
    </content>
  </entry><entry>
    <id>/posts/Write_Reusable_PHP.html</id>
    <title>Write Reusable PHP</title>
    <updated>2021-07-05T00:15:38+00:00</updated>
    <published>2021-07-05T00:15:38+00:00</published>
    <link href="/posts/Write_Reusable_PHP.html"/>
    <summary>Have you ever found yourself writing code with a sense Déjà vu? You feel as though the code you are writing has already been written somewhere else. This could potentially be within the same project or even the same file.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Write Reusable PHP</h1>
<p>Have you ever found yourself writing code with a sense Déjà vu? You feel as though the code you are writing has already been written somewhere else. This could potentially be within the same project or even the same file.</p>
<p>You do not want to be stuck repeating yourself and you do not want to waste time finding answers to the problems you have already solved. You might already be familiar with the &quot;DRY&quot; principle (Don't Repeat Yourself). Repeating code not only makes your project look sloppy and difficult to read (and therefore manage); it increases the likelihood of bugs, and it makes the code more difficult to manage. Here are a few helpful tips for producing PHP code that you can use often, but write only once.</p>
<h2>Use Modules</h2>
<p>Modules are reusable packages that can be included across several of your projects. Modules isolate the functionality of one of your project's features, allowing you to maintain these features separately from your projects.</p>
<p>You might have several PHP-powered websites, most of which connect to social media platforms. Therefore, you might create a module just for managing your social media accounts. Such a module should not be overly concerned with the particular project it is included in, but it instead should only care about storing data related to social media sites.</p>
<p>Perhaps the module would also provide some sort of front-end component to render this data such as icon links to each social network site. If an update were to be added to support some new feature (perhaps a selectable list of icons), then the update would be made to the module itself and not the websites. Any site that uses this module could receive the update by <a href="https://getcomposer.org/">running composer</a>.</p>
<p>Modules are typically namespaced, meaning their classes can only be used in a project when explicitly including it. For example, the full class name for social media links might look something like &quot;Vendor\SocialMedia\SocialMediaLink&quot;. You could use this class directly by prepending the class with a backslash like:</p>
<pre><code>$facebook = new \Vendor\SocialMedia\SocialMediaLink();
</code></pre>
<p>Or you could include the class with the &quot;use&quot; statement:</p>
<pre><code>use Vendor\SocialMedia\SocialMediaLink;
$facebook = new SocialMediaLink();
</code></pre>
<p>Namespacing your modules ensures that its class names never conflict with those that are defined globally or in different modules.</p>
<h2>Write Methods for common actions</h2>
<p>There are some processes that will need to occur in several areas of your project. This could be something like formatting dates. It is usually a good idea to keep a consistent date format across a project.</p>
<p>Your code could have several functions to retrieve dates, but it could be repeating the same code that formats the date in each. It might make sense for you to write a method just for formatting the dates. With one reusable method for date formatting, you will not find yourself writing the same code in several areas.</p>
<p>Sticking to this idea, consider the purpose of each function before writing. If the function requires several separate processes to occur before the main goal is achieved, it would make sense to break each task out into a separate function. Doing this makes your code more readable and more reusable.</p>
<p>For example, your code might include a method that retrieves and renders blog articles that have been marked &quot;Published&quot;. We will call this method &quot;renderPublishedArticles&quot;. If your website has a content management system, it would be a good idea to flag unpublished articles. The code here would likely use a method like &quot;getAllArticles&quot; to list articles in the CMS.</p>
<p>Instead of repeating code to check if articles are published in both methods, a better solution would be to write a method that just checks the &quot;Published&quot; status of an article. This method could be named something like &quot;getPublishedArticles&quot;. The &quot;renderPublishedArticles&quot; and &quot;getAllArticles&quot; methods would both call this method, so the code would otherwise appear in two areas had we not written functions.</p>
<p>Because the functionality has been encapsulated in a function name, we know exactly what is happening when reading the code for each method.</p>
<p>If a change has to be made, the change is made in one area of the code, rather than for each time the code is repeated. In the case of our date formatting method, we might opt for a different date format. You will thank yourself later for writing reusable methods when changes like this are necessary.</p>
<h2>Use Classes to Encapsulate Components</h2>
<p>If you are familiar with Object-Oriented Programming (OOP), you know that you can use classes to encapsulate certain behaviors of your components. In object-oriented programming languages like PHP, you might have a class that represents some type of data model to be rendered. That model will be responsible for defining some data structure, and it will likely provide methods to interact with related components.</p>
<p>For example, you might have a class called &quot;Page&quot;. This model might define fill-able database fields like &quot;HTML Content&quot; and &quot;Featured Images&quot;. This class might also include methods to interact with the objects established in these relationships, such as a &quot;getRecentFeaturedImages&quot; method.</p>
<p>A website will commonly have different types of pages. Some functionality should be shared between all pages, but other pages will have different concerns. For example, a blog page might be responsible for storing and fetching written articles, but a home page will usually feature a large banner image, video, or slideshow. If we want our &quot;Page&quot; class to be extendable, it should only be concerned with the functionality that would be shared across every page type. We could then extend the Page class for each page type and add the appropriate functionality:</p>
<pre><code>class Page
{
 // Define the functionality that should exist on all pages
 ...
}

class HomePage extends Page
{
 // This inherits from &quot;Page&quot;, so just define everything unique to a Home Page
 ...
}
</code></pre>
<h2>Conclusion</h2>
<p>Writing reusable code has a plethora of benefits, as you have seen. Employing these tips means writing code that is consistent everywhere, even across projects.</p>
<p>When many common actions are abstracted into separate methods, the code will naturally become more readable and declarative. Methods will be rewritten to call other methods, each with descriptive names. Your code will naturally become more declarative, and read more like a set of instructions that a human could make sense of.</p>
<p>When functionality needs to be shared between components, having a shared base class that each extends means writing less code overall.</p>
<p>While it might be easy to simply paste in some code that is already used somewhere else, it will be more difficult to maintain later on should things change. Instead, think about how to keep each bit of your code clear in its purpose, so that it is reusable, should you need it for something else tomorrow.</p>

        ]]>
    </content>
  </entry><entry>
    <id>/posts/Take_Your_Web_App_from_React_To_React_Native.html</id>
    <title>Take Your Web App From React To React Native</title>
    <updated>2021-01-26T02:33:05+00:00</updated>
    <published>2021-01-26T02:33:05+00:00</published>
    <link href="/posts/Take_Your_Web_App_from_React_To_React_Native.html"/>
    <summary>Most Web Developers have never had the experience of building a mobile app. The task may seem daunting and outside of the usual comfort zone. You might be starting with no Java or Swift experience. Learning both will take some time, and you would need to manage two codebases. Lucky for us, there is React Native and Expo.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Take Your Web App from React To React Native</h1>
<p>Most Web Developers have never had the experience of building a mobile app. The task may seem daunting and outside of the usual comfort zone. You might be starting with no Java or Swift experience. Learning both will take some time, and you would need to manage two codebases. Lucky for us, there is React Native and Expo.</p>
<p><img alt="" src="images/reactNative.png" height="228" width="456" />Create cross-platform mobile apps with React Native</p>
<p>React Native lets us developers write mobile apps in React, no Java or Swift experience required. Expo further simplifies the development process of React Native applications. It provides libraries for commonly used mobile app components. Expo also sets up a development environment that can even run on your mobile device with the help of the Expo Go app.</p>
<p>Armed with both of these tools, some React experience, and some wit, any web developer can develop their mobile app for iPhone and Android. If you follow these steps, you too can transform your web-based React code into working React Native code.</p>
<p><img alt="" src="images/expo-logo.png" height="201" width="411" />Simplify development. Spend less time getting set up and more time building your app.</p>
<h2>Replace &lt;div&gt;s with &lt;View&gt;s</h2>
<p>The &quot;&lt;div&gt;&quot; tag is perhaps the most commonly used element when it comes to web development. React Native has its own version of &quot;&lt;div&gt;&quot;: the &quot;&lt;View&gt;&quot; tag. Views are used to wrap content areas in your app's layout. Go ahead, copy and paste your web-based React code into your expo app, run a search for &quot;div&quot; and replace it with &quot;View&quot;.</p>
<p>Your project may contain other wrappers such as &quot;&lt;section&gt;&quot; or &quot;&lt;article&gt;&quot;. Be sure to replace these as well. Your content wrappers are now native friendly, and you are one step closer to transitioning your app to mobile. Now, let's take care of the text content.</p>
<h2>Replace Text Elements with &lt;Text&gt; tags</h2>
<p>On the web, there are many different types of text tags. You could have headers in the form of &quot;&lt;h1&gt;&quot; down to &quot;&lt;h6&gt;&quot;. Your project might have text in &quot;&lt;span&gt;&quot;, &quot;&lt;p&gt;&quot;, or &quot;&lt;label&gt;&quot; tags. Anywhere there is text, we will replace the tags with the &quot;&lt;Text&gt;&quot; element. Do this with any plain text in your project, but you can skip any links that use the &quot;&lt;a&gt;&quot; tag for now. This will be handled by a different React Native component, which will be covered next.</p>
<h2>Replace Anchor Tag Links With &lt;Button&gt;s</h2>
<p>Links on the web use the anchor tag: &quot;&lt;a&gt;&quot; with an &quot;href&quot; attribute describing the link's destination. We will replace these links with the React Native &quot;&lt;Button&gt;&quot; element. The text within the link should be moved to the &quot;title&quot; prop of the Button (e.g. &lt;Button title=&quot;Click me&quot;/&gt;).</p>
<p>As for the &quot;href&quot;, you will instead define a function to navigate to a screen. This will be placed within the &quot;onPress&quot; prop. <a href="https://reactnative.dev/docs/navigation">Here is a more in-depth guide</a> on how to do this.</p>
<p>Some links may also pass data in the form of &quot;GET&quot; variables (e.g. &quot;http://example.com?foo=bar&quot;). The variable &quot;foo&quot; has the value of &quot;bar&quot;. In React Native, you can pass data as an object in the second argument of the navigation function. You would instead write something like: &quot;navigation.navigate('Example', {foo: 'bar'})&quot;.</p>
<p>You still may need to update &quot;&lt;input&gt;&quot;s to &quot;&lt;TextInput&gt;&quot;s or &quot;&lt;video&gt;&quot;s to &quot;&lt;Video&gt;&quot;s, but by now you should see that for every web element, there is a React Native equivalent component. Take some time to confirm each HTML tag is replaced with the appropriate component. With all of these changes, your app should be well on its way to working on iPhone and Android.</p>

        ]]>
    </content>
  </entry><entry>
    <id>/posts/Stop_Making_Site_Updates_Harder_For_Yourself_%26_Customers.html</id>
    <title>Stop Making Site Updates Harder For Yourself &amp; Customers</title>
    <updated>2020-09-15T00:41:41+00:00</updated>
    <published>2020-09-15T00:41:41+00:00</published>
    <link href="/posts/Stop_Making_Site_Updates_Harder_For_Yourself_%26_Customers.html"/>
    <summary>Imagine if every time you needed to start your car, you first had to install the engine. No one would ask to borrow your car, but you would be reluctant to use it yourself. No one wants a mundane task to take anymore time and effort than is required. Simple problems should have simple solutions, and updates to our websites should be painless. A site that works on one machine, should work on any machine. A site that needs a single change in punctuation on a single page, should not require a developer.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Stop Making Site Updates Harder For Yourself &amp; Customers</h1>
<p>Imagine if every time you needed to start your car, you first had to install the engine. No one would ask to borrow your car, but you would be reluctant to use it yourself. No one wants a mundane task to take anymore time and effort than is required. Simple problems should have simple solutions, and updates to our websites should be painless. A site that works on one machine, should work on any machine. A site that needs a single change in punctuation on a single page, should not require a developer.</p>
<h2>Use different environments</h2>
<p>Development of a website often begins locally. The site is installed on the same computer the developer works with. When the site is finished, it is often staged in a development environment. The website is accessible from the internet, but it is not yet optimized for the public. When the site is ready to go live, it is installed again in a separate production environment. The site should function nearly identical in all three environments, but certain things should behave differently. In the case of an overlooked fatal error, we wouldn't want to show an end-user a long error trace; only the developer cares about that sort of thing. When entering payment information, we wouldn't want a real transaction to occur during development. Our site should behave differently in subtle ways, depending on our environment. Managing three different codebases for each environment would be highly inefficient and prone to errors. This is where environment variables come into play. With a single configuration file, we can control every setting that should differ in each instance of our site. The code should read the declared environment variables, and decide whether it should actually charge a debit card, or run a fake transaction in a test sandbox environment.</p>
<img alt="" src="images/environment.jpg" height="328" width="493" />
<i>In windy environments, the turbines are more effective at producing power</i>
<h2>Break stuff in isolation</h2>
<p>Start coding on your local environment and break your program in any way you can. Experimentation on local environments should occur often to ensure a robust production environment. Implementing this workflow means that updates for a website are relatively painless for everyone. You and your customers will not have to sweat nervously when making changes that could range from small to dramatic. A developer can work comfortably in a local environment, free to break anything without consequence. Compare this to working on a production site blindly. Both parties risk facing devastating consequences while making changes live and on the fly.</p>
<img alt="" src="images/solar-panel.jpg" height="326" width="492" />
<i>Also a source of power, solar panels are more appropriate in sunny environments</i>
<h2>Use a CMS</h2>
<p>Think about installing your car engine to drive again. If you are a mechanic, while this may feel tedious and time-consuming, you are confident in your ability to start the car. Now, imagine that the only parts of a car that you are familiar with are the brake pedal, gas pedal, and steering wheel. Starting that car is going to feel intimidating and daunting. You may just decide to walk that day.</p>
<p>Your customers do not want to install an engine just to drive. When it comes to their website, content should be updated easily, without any need to touch code. Thankfully, we have Content Management Systems to solve this problem. A developer will write code that will define page types and data objects. This data will make use of templates to be rendered in the layout of the site. From this point, content can be entered in a user-friendly, Graphical User Interface, and then published to the site. A great Content Management System, as well as my personal favorite, is SilverStripe. SilverStripe makes both the developers and customers' lives easier when bringing sites to life. SilverStripe is clean and comprehensible, both in its code and in its user interface. You can check out my tutorials on the SilverStripe CMS and Framework here <a href="https://www.youtube.com/watch?v=XVOianFRSl0">https://www.youtube.com/watch?v=XVOianFRSl0</a></p>
<p><img src="images/silverstripe.png" alt="" />Learn SilverStripe and save everyone some headache</p>
<p>KISS is not only a hard rock band from the 1970s, but a clever acronym used by developers when avoiding complexity. Keep It Simple Stupid. You and your customers do not want risky changes to be made to a site running in production. Your customers want things to be as easy as pie. Give them a CMS that both of you will love. Just remember to keep it simple.</p>

        ]]>
    </content>
  </entry><entry>
    <id>/posts/Tools_I_took_for_granted.html</id>
    <title>Tools I Took For Granted</title>
    <updated>2020-03-08T17:30:08+00:00</updated>
    <published>2020-03-08T17:30:08+00:00</published>
    <link href="/posts/Tools_I_took_for_granted.html"/>
    <summary>With today's modern day-to-day workflow, it is easy to feel spoiled with all of the handy tools at our disposal. Tools like Git allows us to rollback to any previous state of our applications, just in case something were to go horribly wrong. A Content Management System (CMS) allows us to easily update content, without ever touching the code. Package managers like NPM and Composer can provide our project with all of the dependencies it needs simply by running a command. But sometimes, these tools may not be available to you. You might remotely connected to a client's server, which can lack the software you would commonly use in your own environment. And developers are not always working on new code. A majority of time is spent working on existing code. Some of that code may feel severely antiquated from what we are used to.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Tools I took for granted</h1>
<p>With today's modern day-to-day workflow, it is easy to feel spoiled with all of the handy tools at our disposal. Tools like Git allows us to rollback to any previous state of our applications, just in case something were to go horribly wrong. A Content Management System (CMS) allows us to easily update content, without ever touching the code. Package managers like NPM and Composer can provide our project with all of the dependencies it needs simply by running a command. But sometimes, these tools may not be available to you. You might remotely connected to a client's server, which can lack the software you would commonly use in your own environment. And developers are not always working on new code. A majority of time is spent working on existing code. Some of that code may feel severely antiquated from what we are used to.</p>
<h2>Git</h2>
<p>Imagine starting on a new project without Git. Scary right? Git is an essential part of today's software development. However, you may find yourself working on an older project that does not use version control. To safely make changes, a developer would need to create a backup of every file that was changed. That way, if something does break, the developer and client are not left SOL. In this situation, when the changes are ready to go live, it is not as simple as just pulling down from a repository. If you find yourself in this situation, you will need to FTP every file you changed individually. All of this is a huge headache by today's standards. Not only are you at a much higher risk of creating irreversible damage, but you must keep track of each moving part manually.</p>
<h2>Content Management Systems</h2>
<p>Content management systems make customizing the content on pages very simple. With a CMS you can upload content to your site without the need to touch any code. But what if you have a static site made up of several HTML files and no CMS? If you need to make a change to the base layout of your site (headers, main navigations, footers, etc...), that change will need to be made across several files. You could use a search and replace tool (<a href="./rebuilding_my_blog.html">or a static site generator</a>) to make this all happen at once, but what if you have something like a &quot;selected&quot; class on navigation items? Each page will have the same navigation but with a unique selected class for the current page's navigation item. You can start to imagine the headache of editing nearly identical elements across several files. With most content management systems, this is all taken care of.</p>
<h2>Package Managers</h2>
<p>With package managers, you can install all of the code your project needs by running a single command. Older projects may not be fortunate enough to include a file for the package managers that lists the dependencies to be installed. When working on a project like this, you may need to individually initialize and update git submodules (git is great for version control, but use your chose programming languages' most popular package manager please). What's worse is dealing with source code from a separate project, copy, pasted, and committed directly into a different project. In either of these situations, it can be unclear what packages your project requires, and the project will not receive the patches that would be pulled in from a package manager. With tools like Composer and NPM, these concerns are eliminated, so you can spend more time working on your project than resolving issues created from incompatible code.</p>
<h2>Let your tools do the work</h2>
<p>Modern tools make our lives as developers much easier. However, we still find ourselves supporting older projects without these amenities. Working with these systems is far from impossible, but more caution and patience is often required.</p>

        ]]>
    </content>
  </entry><entry>
    <id>/posts/Becoming_part_of_a_team.html</id>
    <title>Becoming Part Of A Team</title>
    <updated>2019-09-14T13:22:52+00:00</updated>
    <published>2019-09-14T13:22:52+00:00</published>
    <link href="/posts/Becoming_part_of_a_team.html"/>
    <summary>I am a few weeks into my first job as a junior web developer. I am thrilled to become part of an active development team. I had practiced and learned on my own, and now I must do so in a team. The first few days have taught me a few things about working in a business setting. I have learned that it is important to keep track of the time that I put into my tasks. I am becoming more familiar with the process of submitting pull requests and receiving code reviews. I have learned to keep track of cards and organize my assignments with Trello.</summary>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[
        <h1>Becoming part of a team</h1>
<p>As of this writing, I am a few weeks into my first job as a junior web developer. I am thrilled to become part of an active development team. I had practiced and learned on my own, and now I must do so in a team. The first few days have taught me a few things about working in a business setting. I have learned that it is important to keep track of the time that I put into my tasks. I am becoming more familiar with the process of submitting pull requests and receiving code reviews. I have learned to keep track of cards and organize my assignments with Trello.</p>
<h2>Doing real work for the real world</h2>
<p>When working on projects for real clients, it is important to be accurate and transparent with the cost of development. It also helps developers have a better estimate on the amount of time a feature will take to build. That is why it is important for me to track my times. I was introduced to a program called &quot;Minute7&quot;. It is a simple and handy tool for tracking time. I simply enter the client, the task, and the times I started and concluded working on a task. It is not overly complex, and it helps those on the business end accurately quote clients and make more informed decisions.</p>
<img alt="" src="images/minute7.jpg" height="170" width="302" />
<h2>Working with teammates</h2>
<p>With my new assignments, I get to practice the process of submitting pull requests. I am more comfortable creating branches for specific requests, and leaving helpful descriptions for the developers reviewing my code. After my code is reviewed, I go back through my code to make the appropriate changes. If everything looks good, I merge and delete the branch I was working on. Git and GitHub are very useful and robust tools especially when working in a team.</p>
<img alt="" src="images/github.jpg" height="154" width="274" />
<p>The team needs to keep track of who does what, so that is where Trello comes in. Trello is what I can use to keep track of the tasks assigned to me, and what the team can use to stay organized with who is doing what. I have learned to provide relevant links to my work, and how some tasks will later be split into tasks with cards of their own. Trello keeps everything organized into separate boards, and it makes it simple to find what needs done.</p>
<img alt="" src="images/Trello.png" height="217" width="217" />
<p>Starting my first job in web development is very exciting. I am learning how a team works to satisfy many clients and create work of high quality. The team must have an estimate on the time an assignment will take, review the code for said assignment, and keep track of who is working on it. It was highly satisfying to complete my first tasks, and I am excited to learn more.</p>

        ]]>
    </content>
  </entry>
</feed>
