<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blogs on Bossagyu Blog</title><link>https://bossagyu.com/en/blog/</link><description>Recent content in Blogs on Bossagyu Blog</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Sun, 19 Apr 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://bossagyu.com/en/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>Three Models of Technical Proficiency: Dreyfus, Shu-Ha-Ri, and the Four Stages of Competence</title><link>https://bossagyu.com/en/blog/050-skill-proficiency-stages/</link><pubDate>Sun, 19 Apr 2026 00:00:00 +0900</pubDate><guid>https://bossagyu.com/en/blog/050-skill-proficiency-stages/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The process of acquiring a skill is rarely uniform. We start by relying on manuals, then learn to judge based on context, and eventually reach a stage where the right action comes without conscious thought. To capture these gradual changes, many models have been proposed over the decades.&lt;/p>
&lt;p>This article introduces three representative frameworks for technical proficiency—the &lt;strong>Dreyfus model&lt;/strong>, &lt;strong>Shu-Ha-Ri&lt;/strong>, and the &lt;strong>Four Stages of Competence&lt;/strong>—and compares their origins, stages, and characteristics. The goal is to offer a toolkit for understanding where we are, designing our next step, and supporting the learning of others.&lt;/p>
&lt;h2 id="why-understand-the-stages-of-proficiency">Why Understand the Stages of Proficiency&lt;/h2>
&lt;p>Thinking of proficiency as a set of stages provides three main benefits.&lt;/p>
&lt;p>First, &lt;strong>self-awareness&lt;/strong>. Knowing which stage you are in clarifies what to work on next. The skills that matter for a beginner (executing procedures correctly) differ greatly from those that matter for a mid-level practitioner (judgment in context). Awareness of stages prevents misdirected effort.&lt;/p>
&lt;p>Second, &lt;strong>learning design&lt;/strong>. Rather than chasing a vague final goal, stage-based milestones make progress easier to sustain and measure. Many models also suggest that the optimal way to learn changes from stage to stage.&lt;/p>
&lt;p>Third, &lt;strong>understanding others&lt;/strong>. In mentoring or team leadership, estimating someone&amp;rsquo;s current stage helps you choose the right support and level of challenge.&lt;/p>
&lt;p>That said, models are not reality. Real learning is continuous and often uneven across domains. The frameworks below are best used as guideposts for thinking, not as rigid categorizations.&lt;/p>
&lt;h2 id="the-dreyfus-model">The Dreyfus Model&lt;/h2>
&lt;h3 id="origin-and-background">Origin and Background&lt;/h3>
&lt;p>The Dreyfus model was proposed in 1980 by brothers Stuart E. Dreyfus and Hubert L. Dreyfus at UC Berkeley, in a report titled &amp;ldquo;A Five-Stage Model of the Mental Activities Involved in Directed Skill Acquisition.&amp;rdquo; Commissioned by the U.S. Air Force Office of Scientific Research, the report drew on pilot training, chess players, drivers, and other skilled practitioners.&lt;/p>
&lt;p>Stuart specialized in operations research while Hubert specialized in philosophy (phenomenology). Together they formalized a key insight: the judgments of experts often cannot be reduced to explicit rules.&lt;/p>
&lt;p>The model later became popular in software engineering thanks to books such as Andy Hunt&amp;rsquo;s &lt;em>Pragmatic Thinking and Learning&lt;/em>, and has become a shared vocabulary for discussing engineer growth.&lt;/p>
&lt;h3 id="five-stages">Five Stages&lt;/h3>
&lt;p>The Dreyfus model describes the journey from novice to expert in five stages.&lt;/p>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">flowchart LR
A[Novice] --> B[Advanced Beginner]
B --> C[Competent]
C --> D[Proficient]
D --> E[Expert]
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;p>&lt;strong>1. Novice&lt;/strong>
Detaches from context and follows given rules. Following the rules is itself challenging, leaving little room to see exceptions or the bigger picture. Think of measuring ingredients from a recipe, or operating the pedals and wheel by the manual when learning to drive.&lt;/p>
&lt;p>&lt;strong>2. Advanced Beginner&lt;/strong>
Begins to notice contextual features through experience, but cannot yet distinguish important features from minor ones, and treats them with equal weight. This is the stage where learners ask many questions and encounter situations their rules don&amp;rsquo;t cover.&lt;/p>
&lt;p>&lt;strong>3. Competent&lt;/strong>
Selects important information from a pool of signals and can set goals and plans on their own. Rules have become internalized. At the same time, learners begin to sense that &amp;ldquo;there is no single right answer,&amp;rdquo; and may feel the weight of decision-making.&lt;/p>
&lt;p>&lt;strong>4. Proficient&lt;/strong>
Grasps situations holistically. Pattern recognition from past similar cases comes first; detailed rule-based reasoning fills in afterward. This is a hybrid stage where intuition and analysis work together.&lt;/p>
&lt;p>&lt;strong>5. Expert&lt;/strong>
Arrives at answers intuitively, embedded in the situation itself, without explicit rule-following. A seasoned doctor estimates a diagnosis at a glance; a master player narrows candidate moves before deliberate analysis. However, when the environment is unusual, even experts fall back on deliberate analysis.&lt;/p>
&lt;h3 id="cognitive-shifts-between-stages">Cognitive Shifts Between Stages&lt;/h3>
&lt;p>The key insight of this model is that as stages progress, the center of cognition shifts from &lt;strong>analysis&lt;/strong> to &lt;strong>pattern recognition&lt;/strong> to &lt;strong>intuition&lt;/strong>. This is not merely a speed-up but a qualitative change in what the practitioner perceives.&lt;/p>
&lt;p>Moving up requires more than rule-following—it requires experience in interpreting rules in context. The Dreyfus brothers repeatedly note that mastery is tacit knowledge that textbooks alone cannot teach.&lt;/p>
&lt;h2 id="shu-ha-ri">Shu-Ha-Ri&lt;/h2>
&lt;h3 id="origin-and-background-1">Origin and Background&lt;/h3>
&lt;p>Shu-Ha-Ri (守破離) is a Japanese model of training stages that emerged from traditional arts such as martial arts, tea ceremony, and flower arrangement. It appears explicitly in &lt;em>Fuhaku Hikki&lt;/em>, a text by the Edo-period tea master Kawakami Fuhaku (1719-1807). Its roots trace back further, to Zeami&amp;rsquo;s (1363-1443) &lt;em>Fūshikaden&lt;/em> and its idea of &lt;em>Jo-Ha-Kyū&lt;/em>, and to the teachings of Sen no Rikyū.&lt;/p>
&lt;p>A poem often attributed to Rikyū reads, &amp;ldquo;Keep the forms of rules and manners until you break them, and even when you leave them, do not forget the origin.&amp;rdquo; This line neatly captures the spirit of Shu-Ha-Ri: preserve the form, then break it, then transcend it—without losing its root.&lt;/p>
&lt;p>In recent years, Shu-Ha-Ri has been applied to Western practices such as agile development, Scrum adoption, and design pattern learning, becoming a shared vocabulary among Japanese-speaking software professionals.&lt;/p>
&lt;h3 id="three-stages">Three Stages&lt;/h3>
&lt;p>Shu-Ha-Ri is a three-stage model built on a master-apprentice relationship.&lt;/p>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">flowchart LR
A[Shu&lt;br/>Follow the form] --> B[Ha&lt;br/>Break the form]
B --> C[Ri&lt;br/>Leave the form]
C -.new form.-> A
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;p>&lt;strong>Shu (守)&lt;/strong>
Practice the forms taught by the master faithfully, without adding personal interpretation. The goal is to imprint the form onto body and mind. The &amp;ldquo;why&amp;rdquo; often comes later; what matters first is precise repetition.&lt;/p>
&lt;p>&lt;strong>Ha (破)&lt;/strong>
Building on the forms absorbed in Shu, the learner incorporates forms from other schools and personal adaptations, breaking the form. This is not mere rebellion but thoughtful comparison and testing. Here, the learner finally feels the reason behind the form.&lt;/p>
&lt;p>&lt;strong>Ri (離)&lt;/strong>
Leave the form and reach an independent mode of being. This is not discarding the form—by now, the form has become part of the self. Externally, the practitioner appears free and unconstrained. The phrase &amp;ldquo;do not forget the origin&amp;rdquo; warns that even at Ri, the root remains in Shu.&lt;/p>
&lt;h3 id="differences-from-western-models">Differences from Western Models&lt;/h3>
&lt;p>Where the Dreyfus model describes changes in an individual&amp;rsquo;s cognition, Shu-Ha-Ri presumes that &lt;strong>forms are passed within a community&lt;/strong>. The form encodes the history of the school and the wisdom of predecessors; inheriting it accurately is the purpose of Shu.&lt;/p>
&lt;p>Shu-Ha-Ri also describes how the learner&amp;rsquo;s &lt;strong>agency&lt;/strong> changes. Reception dominates in Shu, dialogue and testing in Ha, creation in Ri. The model implies a maturation of the whole person, not only a skill upgrade.&lt;/p>
&lt;h3 id="modern-applications">Modern Applications&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Learning design patterns&lt;/strong>: Apply patterns literally (Shu), adapt them to project context (Ha), and eventually design from first principles without needing patterns (Ri).&lt;/li>
&lt;li>&lt;strong>Scrum adoption&lt;/strong>: Start by following the Scrum Guide strictly, then adapt events and roles once the team has matured.&lt;/li>
&lt;li>&lt;strong>Onboarding in general&lt;/strong>: As a guide for how a mentor&amp;rsquo;s stance should shift from prescribing procedures to reviewing decisions to co-creating systems.&lt;/li>
&lt;/ul>
&lt;h2 id="the-four-stages-of-competence">The Four Stages of Competence&lt;/h2>
&lt;h3 id="origin-and-background-2">Origin and Background&lt;/h3>
&lt;p>The &amp;ldquo;Four Stages of Competence&amp;rdquo; (also known as the Conscious Competence Ladder) is commonly attributed to Noel Burch of Gordon Training International in the 1970s. However, similar ideas are sometimes attributed to Abraham Maslow or Thomas Gordon, so the origin is not entirely settled.&lt;/p>
&lt;p>The distinctive feature of this model is that it combines two axes: whether a learner &lt;strong>has a capability&lt;/strong>, and whether they are &lt;strong>aware of having or lacking it&lt;/strong>. By focusing on awareness as much as ability, the model is particularly good at describing the learner&amp;rsquo;s psychological state.&lt;/p>
&lt;h3 id="four-stages">Four Stages&lt;/h3>
&lt;p>The four stages can be organized as a 2x2 matrix.&lt;/p>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">quadrantChart
title Four Stages of Competence
x-axis Low capability --> High capability
y-axis Unaware --> Aware
quadrant-1 Conscious Competence
quadrant-2 Conscious Incompetence
quadrant-3 Unconscious Incompetence
quadrant-4 Unconscious Competence
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;p>&lt;strong>1. Unconscious Incompetence&lt;/strong>
The learner is not aware of what they cannot do, and thus sees no need to learn. Escape from this stage usually requires external feedback or a vivid encounter with one&amp;rsquo;s own limits.&lt;/p>
&lt;p>&lt;strong>2. Conscious Incompetence&lt;/strong>
The learner recognizes their inability. This is psychologically the hardest stage, where self-doubt can derail learning. It helps to have an environment that offers small wins and a relationship that makes failure safe.&lt;/p>
&lt;p>&lt;strong>3. Conscious Competence&lt;/strong>
The learner can perform, but only with deliberate effort and attention. Execution is slow and tiring, yet consistently produces results. Repetition here builds the foundation for the next stage.&lt;/p>
&lt;p>&lt;strong>4. Unconscious Competence&lt;/strong>
The learner performs naturally, without conscious thought. Attention is freed for other things. A new challenge emerges: because the skill is tacit, it becomes hard to explain &amp;ldquo;why&amp;rdquo; to others.&lt;/p>
&lt;h3 id="psychological-significance-and-a-fifth-stage">Psychological Significance and a Fifth Stage&lt;/h3>
&lt;p>Unlike other models, this one explicitly names the &lt;strong>valley of Conscious Incompetence&lt;/strong>. Learning starts with confronting one&amp;rsquo;s incompetence; how well we support learners at this stage largely determines whether they continue.&lt;/p>
&lt;p>Some variants add a fifth stage: &lt;strong>Conscious Competence of Unconscious Competence&lt;/strong>, sometimes called &amp;ldquo;reflective competence.&amp;rdquo; This is the state of being able to re-examine one&amp;rsquo;s tacit skill and articulate it for others—a state mentors and teachers aspire to.&lt;/p>
&lt;h2 id="comparing-the-three-frameworks">Comparing the Three Frameworks&lt;/h2>
&lt;p>At first glance the three models look similar, but they focus on different aspects.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Dimension&lt;/th>
&lt;th>Dreyfus Model&lt;/th>
&lt;th>Shu-Ha-Ri&lt;/th>
&lt;th>Four Stages of Competence&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Number of stages&lt;/td>
&lt;td>5&lt;/td>
&lt;td>3&lt;/td>
&lt;td>4 (5 in variants)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Focus&lt;/td>
&lt;td>Quality of judgment and action&lt;/td>
&lt;td>Relationship to form&lt;/td>
&lt;td>Combination of ability and awareness&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cultural background&lt;/td>
&lt;td>Western / phenomenology&lt;/td>
&lt;td>Japanese / traditional arts&lt;/td>
&lt;td>Western / education psychology&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Period of formulation&lt;/td>
&lt;td>1980&lt;/td>
&lt;td>Edo period (with older roots)&lt;/td>
&lt;td>1970s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Implied learner&lt;/td>
&lt;td>An individual cognition changing&lt;/td>
&lt;td>A member inheriting form within a community&lt;/td>
&lt;td>A self moving between conscious and unconscious&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Top-stage ideal&lt;/td>
&lt;td>Intuitive judgment&lt;/td>
&lt;td>Free action beyond form&lt;/td>
&lt;td>Automatic, effortless execution&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="commonalities">Commonalities&lt;/h3>
&lt;p>Beneath the surface, the models share structural features.&lt;/p>
&lt;ul>
&lt;li>A direction from &lt;strong>external dependence to autonomy&lt;/strong>. Rules, forms, or conscious effort are the starting point; they become internalized over time.&lt;/li>
&lt;li>The top stage is described as &lt;strong>mastery that resists easy verbalization&lt;/strong>. Dreyfus&amp;rsquo;s Expert, Shu-Ha-Ri&amp;rsquo;s Ri, and the Four Stages&amp;rsquo; Unconscious Competence all point to territory beyond explicit instruction.&lt;/li>
&lt;li>Progression is &lt;strong>not linear&lt;/strong>. Earlier skills remain as foundations rather than being discarded.&lt;/li>
&lt;/ul>
&lt;h3 id="differences">Differences&lt;/h3>
&lt;p>The points of focus clearly diverge.&lt;/p>
&lt;ul>
&lt;li>The &lt;strong>Dreyfus model&lt;/strong> focuses on the quality of judgment, tracing the cognitive shift from rule-application to intuitive pattern recognition.&lt;/li>
&lt;li>&lt;strong>Shu-Ha-Ri&lt;/strong> focuses on relationship with form and the community that transmits it.&lt;/li>
&lt;li>The &lt;strong>Four Stages of Competence&lt;/strong> focuses on states of awareness, especially the pivot of realizing one&amp;rsquo;s own incompetence.&lt;/li>
&lt;/ul>
&lt;h3 id="which-to-use-when">Which to Use When&lt;/h3>
&lt;ul>
&lt;li>To analyze patterns of behavior and judgment → the &lt;strong>Dreyfus model&lt;/strong>&lt;/li>
&lt;li>To describe a learning environment with a master, mentor, or shared form → &lt;strong>Shu-Ha-Ri&lt;/strong>&lt;/li>
&lt;li>To support learners psychologically, especially near the valley of doubt → the &lt;strong>Four Stages of Competence&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>They complement each other, and it is often useful to view a single learner through multiple lenses.&lt;/p>
&lt;h2 id="applications-learning-and-product-design">Applications: Learning and Product Design&lt;/h2>
&lt;p>These frameworks are useful not only as knowledge but as tools. Three common applications:&lt;/p>
&lt;h3 id="1-designing-your-own-learning">1. Designing Your Own Learning&lt;/h3>
&lt;p>Assess your current stage through multiple models. If you look solid as a Competent on Dreyfus but are still at Conscious Competence on the awareness axis, you have discovered a concrete next step. Noticing that you lack &amp;ldquo;experience breaking the form&amp;rdquo; can motivate you to seek exposure to other schools (other teams, tools, or languages).&lt;/p>
&lt;h3 id="2-mentoring-and-supporting-others">2. Mentoring and Supporting Others&lt;/h3>
&lt;p>Different stages call for different involvement.&lt;/p>
&lt;ul>
&lt;li>For novices and the unconsciously incompetent: provide explicit rules and a safe environment for repetition.&lt;/li>
&lt;li>For the consciously incompetent: stack small wins and affirm that failure is part of the process.&lt;/li>
&lt;li>For Competent learners / those at Ha: hand over decision-making responsibility and support through decision reviews.&lt;/li>
&lt;li>For Proficient, Expert, or Ri learners: create opportunities to articulate their tacit knowledge for others.&lt;/li>
&lt;/ul>
&lt;p>Misjudging the stage turns help into imposition or detachment. Being stage-aware improves the precision of involvement.&lt;/p>
&lt;h3 id="3-product-and-app-design">3. Product and App Design&lt;/h3>
&lt;p>These models also inform how we design for users with varying levels of expertise.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Onboarding&lt;/strong>: Provide explicit guides, step-by-step flows, and constraints for beginners (the Shu stage).&lt;/li>
&lt;li>&lt;strong>Mode switching&lt;/strong>: Offer shortcuts and advanced modes for experienced users, and reduce boilerplate hints (Ha and Ri).&lt;/li>
&lt;li>&lt;strong>Supporting Conscious Incompetence&lt;/strong>: Users who &amp;ldquo;don&amp;rsquo;t know what they don&amp;rsquo;t know&amp;rdquo; benefit from diagnostics and tutorials that point to what needs learning.&lt;/li>
&lt;li>&lt;strong>Feedback that promotes stage transitions&lt;/strong>: Rather than just returning errors, show hints for the learner&amp;rsquo;s next step.&lt;/li>
&lt;/ul>
&lt;p>Viewing a product through these lenses tends to clarify feature priorities and information architecture.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>Among the many frameworks describing technical proficiency, the Dreyfus model, Shu-Ha-Ri, and the Four Stages of Competence offer complementary lenses.&lt;/p>
&lt;ul>
&lt;li>The Dreyfus model captures &lt;strong>changes in the quality of judgment&lt;/strong>.&lt;/li>
&lt;li>Shu-Ha-Ri captures &lt;strong>changes in the relationship with form&lt;/strong>.&lt;/li>
&lt;li>The Four Stages of Competence captures &lt;strong>the interplay of awareness and ability&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>Models are maps, not the territory. Real learners change continuously, and their stages can vary across domains. Still, a map helps us estimate where we are and choose the next step. Use these frameworks as situation-appropriate tools—for your own learning, your relationships with others, and the products you build.&lt;/p>
&lt;h3 id="references">References&lt;/h3>
&lt;ul>
&lt;li>Stuart E. Dreyfus, Hubert L. Dreyfus, &amp;ldquo;A Five-Stage Model of the Mental Activities Involved in Directed Skill Acquisition&amp;rdquo; (1980, U.S. Air Force Office of Scientific Research)&lt;/li>
&lt;li>Andy Hunt, &lt;em>Pragmatic Thinking and Learning: Refactor Your Wetware&lt;/em> (2008, Pragmatic Bookshelf)&lt;/li>
&lt;li>Kawakami Fuhaku, &lt;em>Fuhaku Hikki&lt;/em>&lt;/li>
&lt;li>Zeami, &lt;em>Fūshikaden&lt;/em>&lt;/li>
&lt;li>Noel Burch, &amp;ldquo;The Four Stages for Learning Any New Skill&amp;rdquo; (1970s, Gordon Training International)&lt;/li>
&lt;/ul></description></item><item><title>Getting Started with FastAPI: Building a CRUD API Tutorial</title><link>https://bossagyu.com/en/blog/049-fastapi-crud/</link><pubDate>Sat, 28 Feb 2026 00:00:00 +0900</pubDate><guid>https://bossagyu.com/en/blog/049-fastapi-crud/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>&lt;a class="link" href="https://fastapi.tiangolo.com/" target="_blank" rel="noopener"
>FastAPI&lt;/a> is a high-performance web framework for building APIs with Python. Its key features include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Type hint-based&lt;/strong>: Leverages Python type hints for automatic request/response validation&lt;/li>
&lt;li>&lt;strong>Auto-generated documentation&lt;/strong>: Swagger UI (OpenAPI) is automatically generated, allowing you to test APIs from the browser&lt;/li>
&lt;li>&lt;strong>High performance&lt;/strong>: Built on Starlette + Pydantic, delivering performance comparable to Node.js and Go&lt;/li>
&lt;/ul>
&lt;p>In this article, we&amp;rsquo;ll walk through building a TODO app CRUD API using FastAPI.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;ul>
&lt;li>Python 3.11 or higher&lt;/li>
&lt;li>uv installed (see &lt;a class="link" href="https://bossagyu.com/en/blog/032-python-uv/" >Setting Up a Python Development Environment on Mac with UV&lt;/a>)&lt;/li>
&lt;/ul>
&lt;h2 id="environment-setup">Environment Setup&lt;/h2>
&lt;p>First, create a project and install the required packages.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uv init fastapi-todo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> fastapi-todo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">uv add fastapi uvicorn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>fastapi&lt;/code> is the web framework itself, and &lt;code>uvicorn&lt;/code> is the ASGI server.&lt;/p>
&lt;h2 id="hello-world">Hello World&lt;/h2>
&lt;p>Let&amp;rsquo;s start by verifying FastAPI works with a minimal setup. Create a &lt;code>main.py&lt;/code> file.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fastapi&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FastAPI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">read_root&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello, FastAPI!&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Start the server:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uv run uvicorn main:app --reload
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The &lt;code>--reload&lt;/code> option automatically reloads the server when code changes are detected.&lt;/p>
&lt;p>Open http://localhost:8000 in your browser and you&amp;rsquo;ll see:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello, FastAPI!&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="swagger-ui">Swagger UI&lt;/h3>
&lt;p>One of FastAPI&amp;rsquo;s greatest features is its auto-generated API documentation. Visit http://localhost:8000/docs to see the Swagger UI.&lt;/p>
&lt;p>You can test APIs directly from here, eliminating the need for curl or Postman during development.&lt;/p>
&lt;h2 id="implementing-the-crud-api">Implementing the CRUD API&lt;/h2>
&lt;p>Let&amp;rsquo;s implement the CRUD API for a TODO app.&lt;/p>
&lt;h3 id="defining-models">Defining Models&lt;/h3>
&lt;p>First, define the TODO data structure using Pydantic models. Replace &lt;code>main.py&lt;/code> with the following:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fastapi&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">HTTPException&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseModel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TodoCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">title&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TodoResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">title&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">next_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>TodoCreate&lt;/code>: Request body schema for creating and updating TODOs&lt;/li>
&lt;li>&lt;code>TodoResponse&lt;/code>: Response type definition&lt;/li>
&lt;li>&lt;code>todos&lt;/code>: In-memory data store (dict)&lt;/li>
&lt;li>&lt;code>next_id&lt;/code>: Auto-incrementing ID counter&lt;/li>
&lt;/ul>
&lt;h3 id="create-post">Create (POST)&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">create_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">TodoCreate&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">global&lt;/span> &lt;span class="n">next_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todo_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">next_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completed&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">next_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">next_id&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>By specifying &lt;code>response_model=TodoResponse&lt;/code>, the response type is reflected in the Swagger UI.&lt;/p>
&lt;h3 id="list-get">List (GET)&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">list_todos&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todos&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">values&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="get-by-id-get">Get by ID (GET)&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Returns a 404 error via &lt;code>HTTPException&lt;/code> when the ID doesn&amp;rsquo;t exist.&lt;/p>
&lt;h3 id="update-put">Update (PUT)&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">update_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">TodoCreate&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todo_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completed&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="delete-delete">Delete (DELETE)&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">delete_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">del&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Todo deleted&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="complete-code">Complete Code&lt;/h3>
&lt;p>Here&amp;rsquo;s the full &lt;code>main.py&lt;/code> with all endpoints combined:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fastapi&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">HTTPException&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseModel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TodoCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">title&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TodoResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">title&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">next_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">create_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">TodoCreate&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">global&lt;/span> &lt;span class="n">next_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todo_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">next_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completed&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">next_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">next_id&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">list_todos&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todos&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">values&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">update_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">TodoCreate&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todo_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completed&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">delete_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">del&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Todo deleted&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="testing">Testing&lt;/h2>
&lt;p>Start the server and test through the Swagger UI.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uv run uvicorn main:app --reload
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Visit http://localhost:8000/docs and test in the following order:&lt;/p>
&lt;h3 id="1-create-a-todo">1. Create a TODO&lt;/h3>
&lt;p>Open &lt;code>POST /todos&lt;/code>, click &amp;ldquo;Try it out&amp;rdquo;, and enter:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Go shopping&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Click &amp;ldquo;Execute&amp;rdquo; to get a response like:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Go shopping&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-list-todos">2. List TODOs&lt;/h3>
&lt;p>Execute &lt;code>GET /todos&lt;/code> to see the list of created TODOs.&lt;/p>
&lt;h3 id="3-update">3. Update&lt;/h3>
&lt;p>Use &lt;code>PUT /todos/1&lt;/code> to change &lt;code>completed&lt;/code> to &lt;code>true&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Go shopping&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="4-delete">4. Delete&lt;/h3>
&lt;p>Execute &lt;code>DELETE /todos/1&lt;/code> to delete the TODO. Verify with &lt;code>GET /todos&lt;/code> that an empty array is returned.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>In this article, we built a TODO CRUD API using FastAPI. Here are the key advantages:&lt;/p>
&lt;ul>
&lt;li>Automatic request/response validation with Pydantic models&lt;/li>
&lt;li>Interactive API documentation via Swagger UI&lt;/li>
&lt;li>Simple API specification definition using just Python type hints&lt;/li>
&lt;/ul>
&lt;p>We used an in-memory data store in this tutorial, but in real applications, you would use an ORM like SQLAlchemy to connect to a database.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/032-python-uv/" >Setting Up a Python Development Environment on Mac with UV&lt;/a> - Python environment setup&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/048-lawve-backend/" >Event-Driven Backend for Syncing GCS with Gemini File Search API&lt;/a> - Practical FastAPI example&lt;/li>
&lt;/ul></description></item><item><title>Designing an Event-Driven Backend to Sync GCS with Gemini File Search API</title><link>https://bossagyu.com/en/blog/048-lawve-backend/</link><pubDate>Sun, 15 Feb 2026 00:00:00 +0900</pubDate><guid>https://bossagyu.com/en/blog/048-lawve-backend/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>I participated in the &amp;ldquo;Law x Digital&amp;rdquo; Hackathon (3rd edition) hosted by Japan&amp;rsquo;s Digital Agency and developed a cross-source legal document search product called &amp;ldquo;Lawve.&amp;rdquo; Lawve enables natural language search across e-Gov legal data and user-uploaded documents.&lt;/p>
&lt;p>This article focuses on the backend architecture design that I was responsible for. The backend&amp;rsquo;s primary responsibility is to &amp;ldquo;automatically sync the state of Gemini File Search API whenever files are added to or removed from Google Cloud Storage (GCS).&amp;rdquo;&lt;/p>
&lt;h2 id="system-architecture">System Architecture&lt;/h2>
&lt;p>Here is the overall system architecture of Lawve.&lt;/p>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">flowchart TB
subgraph User
U[Browser]
end
subgraph GCP
subgraph Frontend
FE[Cloud Run&lt;br/>Next.js]
end
subgraph Backend
CR[Cloud Run&lt;br/>FastAPI]
end
subgraph Storage &amp; Data
GCS[(Cloud Storage)]
FS[(Firestore)]
end
subgraph Event Infrastructure
EA[Eventarc]
end
subgraph AI
GEMINI[Gemini File&lt;br/>Search API]
end
end
subgraph External API
EGOV[e-Gov&lt;br/>Law API]
end
U --> FE
FE --> GEMINI
FE --> FS
FE --> EGOV
FE -->|File Upload| GCS
GCS -->|Event Notification| EA
EA -->|CloudEvents| CR
CR -->|Register/Delete| GEMINI
CR -->|Download| GCS
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;h3 id="component-roles">Component Roles&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Role&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Cloud Run (Next.js)&lt;/td>
&lt;td>Frontend: search UI, file upload, search result display&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cloud Run (FastAPI)&lt;/td>
&lt;td>Backend: syncs Gemini File Search API in response to GCS events&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cloud Storage&lt;/td>
&lt;td>Document storage, serves as the Single Source of Truth&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Eventarc&lt;/td>
&lt;td>Routes GCS file change events to Cloud Run&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Gemini File Search API&lt;/td>
&lt;td>Provides full-text and semantic search for documents&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Firestore&lt;/td>
&lt;td>Manages document metadata and comments&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>e-Gov Law API&lt;/td>
&lt;td>Retrieves legal data&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>This article focuses on the backend Cloud Run (FastAPI) design.&lt;/p>
&lt;h2 id="event-driven-architecture-design">Event-Driven Architecture Design&lt;/h2>
&lt;h3 id="why-event-driven">Why Event-Driven?&lt;/h3>
&lt;p>In a legal document search product, documents need to be automatically registered with Gemini File Search API when placed in GCS. While a synchronous API approach from the frontend was an option, I chose an event-driven architecture for the following reasons:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Loose coupling&lt;/strong>: The frontend only needs to place files in GCS without knowing about the backend&lt;/li>
&lt;li>&lt;strong>Reliability&lt;/strong>: Eventarc reliably detects GCS events and delivers them to Cloud Run&lt;/li>
&lt;li>&lt;strong>Tool compatibility&lt;/strong>: Files placed via CLI or scripts are synced in the same way&lt;/li>
&lt;/ul>
&lt;h3 id="event-routing-with-eventarc">Event Routing with Eventarc&lt;/h3>
&lt;p>Eventarc monitors two types of GCS events and routes them to the Cloud Run endpoint.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Event&lt;/th>
&lt;th>Trigger Condition&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>google.cloud.storage.object.v1.finalized&lt;/code>&lt;/td>
&lt;td>When a file is uploaded to GCS&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>google.cloud.storage.object.v1.deleted&lt;/code>&lt;/td>
&lt;td>When a file is deleted from GCS&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The Cloud Run endpoint receives events in CloudEvents format.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">handle_storage_event&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">headers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">body&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">event&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">from_http&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">event_handler&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">process&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="file-upload-processing-flow">File Upload Processing Flow&lt;/h3>
&lt;p>When a file is placed in GCS, it is processed through the following flow:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Event reception and path filtering&lt;/strong>: Only files with the &lt;code>file-search/archive/&lt;/code> prefix are processed&lt;/li>
&lt;li>&lt;strong>Metadata extraction&lt;/strong>: Automatically extract &lt;code>law_id&lt;/code> and &lt;code>source_type&lt;/code> from the GCS path&lt;/li>
&lt;li>&lt;strong>Delete existing documents&lt;/strong>: Remove documents with the same &lt;code>source_path&lt;/code> to prevent duplicates&lt;/li>
&lt;li>&lt;strong>File download&lt;/strong>: Download from GCS to a local temporary file&lt;/li>
&lt;li>&lt;strong>Register with File Search Store&lt;/strong>: Upload to Gemini File Search API with metadata&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">EventHandler&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_handle_event_by_type&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event_type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_name&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">event_type&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;google.cloud.storage.object.v1.finalized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_handle_file_upload&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">event_type&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;google.cloud.storage.object.v1.deleted&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_handle_file_delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="file-deletion-processing-flow">File Deletion Processing Flow&lt;/h3>
&lt;p>When a file is deleted from GCS, the corresponding document is also deleted from Gemini File Search API.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Event reception and path filtering&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Search File Search Store by metadata&lt;/strong>: Identify the matching document using the &lt;code>source_path&lt;/code> metadata&lt;/li>
&lt;li>&lt;strong>Delete document&lt;/strong>: Remove the document from File Search Store&lt;/li>
&lt;/ol>
&lt;h2 id="integration-with-gemini-file-search-api">Integration with Gemini File Search API&lt;/h2>
&lt;h3 id="unified-gcp-ecosystem">Unified GCP Ecosystem&lt;/h3>
&lt;p>Lawve adopts a policy of unifying the entire infrastructure on GCP. While other RAG services such as OpenAI and Pinecone were options, I chose Gemini File Search API for its seamless integration with Cloud Run, GCS, and Eventarc.&lt;/p>
&lt;h3 id="file-search-store-overview">File Search Store Overview&lt;/h3>
&lt;p>Gemini File Search Store is a managed service that vectorizes and indexes registered documents to provide semantic search. The backend performs the following operations:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Document registration&lt;/strong>: Upload files with metadata&lt;/li>
&lt;li>&lt;strong>Document search&lt;/strong>: Search existing documents by metadata&lt;/li>
&lt;li>&lt;strong>Document deletion&lt;/strong>: Remove documents that are no longer needed&lt;/li>
&lt;/ul>
&lt;h3 id="metadata-based-management">Metadata-Based Management&lt;/h3>
&lt;p>Each document is tagged with three metadata fields for management purposes.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Metadata&lt;/th>
&lt;th>Purpose&lt;/th>
&lt;th>Example&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>law_id&lt;/code>&lt;/td>
&lt;td>Legal document ID for unique identification&lt;/td>
&lt;td>&lt;code>323AC0000000205&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>source_type&lt;/code>&lt;/td>
&lt;td>Document classification&lt;/td>
&lt;td>&lt;code>user&lt;/code>, &lt;code>doc&lt;/code>, &lt;code>admin&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>source_path&lt;/code>&lt;/td>
&lt;td>Full GCS path for unique document identification&lt;/td>
&lt;td>&lt;code>gs://bucket/file-search/archive/user/323AC0000000205/medical-law.txt&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Since &lt;code>source_path&lt;/code> is the GCS path itself, it uniquely maps GCS files to File Search Store documents.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">metadata&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;law_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">law_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;source_path&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">source_path&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;source_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">source_type&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">custom_metadata&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;key&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;string_value&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="design-patterns-for-extensibility">Design Patterns for Extensibility&lt;/h2>
&lt;h3 id="path-based-metadata-extraction">Path-Based Metadata Extraction&lt;/h3>
&lt;p>The backend is designed to process any file placed in a specific GCS path, not limited to legal documents. The path convention is as follows:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">gs://&amp;lt;bucket&amp;gt;/file-search/archive/&amp;lt;source_type&amp;gt;/&amp;lt;law_id&amp;gt;/&amp;lt;file_name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The &lt;code>source_type&lt;/code> and &lt;code>law_id&lt;/code> are automatically extracted from the path using a regular expression.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Path pattern&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">GCS_PATH_PATTERN&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;file-search/archive/([^/]+)/([^/]+)/.+&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">parse_gcs_path&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gcs_path&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Tuple&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">match&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GCS_PATH_PATTERN&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">gcs_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This design allows any type of document, such as internal documents or technical documentation, to be managed through the same mechanism. You can add new classifications simply by changing the &lt;code>source_type&lt;/code>, and no backend code changes are required as long as the path convention is followed.&lt;/p>
&lt;h3 id="guaranteeing-state-synchronization-between-gcs-and-gemini-file-search-api">Guaranteeing State Synchronization Between GCS and Gemini File Search API&lt;/h3>
&lt;p>GCS serves as the Single Source of Truth, and the design principle is to always keep the File Search Store state in sync with GCS.&lt;/p>
&lt;p>&lt;strong>Idempotent uploads&lt;/strong>: When the same file is re-uploaded, existing documents are deleted before re-registration. This prevents duplicates while supporting content updates.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">process_file_upload&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">source_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;gs://&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Check and delete existing documents&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_delete_existing_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># New upload&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">law_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extract_law_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">local_file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gcs_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">download_to_temp_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">success&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">store_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">upload_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">local_file_path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">success&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Cascading deletes&lt;/strong>: When a file is deleted from GCS, the corresponding document is automatically removed from the File Search Store. The system searches by &lt;code>source_path&lt;/code> metadata and deletes the matching document.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">process_file_delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">source_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;gs://&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">store_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">find_documents_by_source_path&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">documents&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">store_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">delete_document&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="local-download-design">Local Download Design&lt;/h3>
&lt;p>Registration from GCS to Gemini File Search API is done by first downloading to a local temporary file.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">download_to_temp_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">splitext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">ext&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;.txt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">temp_fd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">temp_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tempfile&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mkstemp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">suffix&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ext&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">close&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">temp_fd&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">blob&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">download_to_filename&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">temp_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">temp_path&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>While direct upload would be simpler, downloading locally first provides the following benefits:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Extensibility&lt;/strong>: Enables future file transformation steps such as converting Excel to CSV before upload&lt;/li>
&lt;li>&lt;strong>MIME detection&lt;/strong>: Preserving the file extension enables accurate MIME type detection&lt;/li>
&lt;li>&lt;strong>Debugging&lt;/strong>: Allows inspection of local file contents when issues occur&lt;/li>
&lt;/ul>
&lt;h2 id="error-handling-and-operational-design">Error Handling and Operational Design&lt;/h2>
&lt;h3 id="always-return-200-ok">Always Return 200 OK&lt;/h3>
&lt;p>The Cloud Run endpoint always returns 200 OK to Eventarc requests, even when errors occur.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">handle_storage_event&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">headers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">body&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">event&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">from_http&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">event_handler&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">process&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;error&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Event processing failed: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;note&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Error logged and notified via Slack&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This design prevents Eventarc retries. If the endpoint returns 4xx/5xx, Eventarc resends the event, causing the same error to repeat. This results in duplicate Slack notifications and polluted logs. By returning 200 OK and handling errors through Slack notifications and logging, operational issues are avoided.&lt;/p>
&lt;h3 id="error-monitoring-via-slack-notifications">Error Monitoring via Slack Notifications&lt;/h3>
&lt;p>When a File Search Store upload fails, an error notification is sent via Slack Webhook. The notification includes the GCS path and error details, enabling operators to quickly identify the problem.&lt;/p>
&lt;p>While this Slack-based monitoring is sufficient for hackathon-scale development, a production environment would require more robust monitoring and recovery mechanisms, such as Cloud Monitoring alerts, Cloud Logging integration, and Dead Letter Queues for reprocessing failed events.&lt;/p>
&lt;h3 id="lazy-initialization-for-client-management">Lazy Initialization for Client Management&lt;/h3>
&lt;p>Gemini API clients and GCS clients use lazy initialization.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@property&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Client&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_client&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_client&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This allows the application to start even without API keys configured, making it easier to work in test environments or local development without providing all environment variables.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>The Lawve backend was built on the following design principles:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Loosely coupled automatic sync via event-driven architecture&lt;/strong>: Using Eventarc to keep the frontend and backend decoupled while automatically reflecting GCS changes in Gemini File Search API&lt;/li>
&lt;li>&lt;strong>Metadata-driven extensible design&lt;/strong>: Automatically extracting metadata from path conventions to support documents beyond legal data&lt;/li>
&lt;li>&lt;strong>GCS as Single Source of Truth&lt;/strong>: Idempotent uploads and cascading deletes ensure the GCS and File Search Store states always match&lt;/li>
&lt;/ul>
&lt;p>By combining an event-driven architecture with GCP managed services, I was able to build a reliable document synchronization platform with minimal code. The simplicity of making documents searchable just by placing files in GCS was a significant advantage during the time-limited hackathon development.&lt;/p>
&lt;p>The source code is available on &lt;a class="link" href="https://github.com/23-u-don/lawve-backend" target="_blank" rel="noopener"
>GitHub&lt;/a>.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/032-python-uv/" >Setting Up a Python Development Environment on Mac with UV&lt;/a> (Python environment setup)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/044-openai-response-api/" >How to Use the OpenAI Response API&lt;/a> (using OpenAI API)&lt;/li>
&lt;/ul></description></item><item><title>Building a Cleaning Reminder Bot with AWS Lambda + LINE</title><link>https://bossagyu.com/en/blog/046-clean-bot-technical/</link><pubDate>Sat, 07 Feb 2026 19:00:00 +0900</pubDate><guid>https://bossagyu.com/en/blog/046-clean-bot-technical/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>I built a cleaning reminder LINE Bot for family use. This article explains the technical implementation details.&lt;/p>
&lt;p>See the completed Bot here: &lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >Cleaning Reminder Bot&lt;/a>&lt;/p>
&lt;h2 id="architecture">Architecture&lt;/h2>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">flowchart LR
subgraph User
A[LINE User]
end
subgraph AWS
B[API Gateway]
C[Lambda&lt;br/>process_user_message]
D[(S3&lt;br/>JSON)]
E[Lambda&lt;br/>push_message_periodically]
F[EventBridge&lt;br/>hourly]
end
A -->|Send message| B
B --> C
C &lt;--> D
E &lt;--> D
F -->|Trigger| E
E -->|Notify| A
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;h3 id="services-used">Services Used&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Service&lt;/th>
&lt;th>Purpose&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>API Gateway (HTTP API)&lt;/td>
&lt;td>Receive LINE Webhooks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Lambda&lt;/td>
&lt;td>Message processing, scheduled notifications&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>S3&lt;/td>
&lt;td>Store per-user task data&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>EventBridge&lt;/td>
&lt;td>Scheduled execution&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="why-this-architecture">Why This Architecture&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Serverless&lt;/strong>: Pay only for what you use, zero operational overhead&lt;/li>
&lt;li>&lt;strong>S3&lt;/strong>: Cheaper than RDS, simple JSON management&lt;/li>
&lt;li>&lt;strong>SAM&lt;/strong>: Infrastructure as Code for deployment management&lt;/li>
&lt;/ul>
&lt;h2 id="project-structure">Project Structure&lt;/h2>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">clean-bot/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── lib/ # Core modules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── clean_task.py # Task state management
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── message.py # Command parser
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── line.py # LINE API
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── s3_client.py # S3 operations
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── test/ # Tests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── line_clean_bot.py # Lambda entry point
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── template.yaml # SAM template
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── Makefile # Development commands
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="implementation-details">Implementation Details&lt;/h2>
&lt;h3 id="1-lambda-entry-points">1. Lambda Entry Points&lt;/h3>
&lt;p>Two Lambda functions are defined.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># line_clean_bot.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">process_user_message&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;LINE message webhook handler&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">body&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">loads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;body&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Get user ID or group ID&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;events&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;source&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;type&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;user&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">line_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;events&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;source&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;userId&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">line_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;events&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;source&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;groupId&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;events&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;message&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;text&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Fetch user data from S3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">S3client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BUCKET_NAME&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">obj_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">line_id&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;.json&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">check_exist_object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">obj_key&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">update_object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">obj_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;{&amp;#34;tasks&amp;#34;: []}&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Process message and reply&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">push_message_periodically&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Send reminder notifications on schedule&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">current_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">timedelta&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hours&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">9&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># JST&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">S3client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BUCKET_NAME&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">obj_list&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">list_objects&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">clean_task&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">CleanTask&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_object_body&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">obj_list&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Check if notification conditions are met&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">clean_task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">should_notify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">current_time&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Send message&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-task-deadline-management">2. Task Deadline Management&lt;/h3>
&lt;p>Tasks are evaluated based on &amp;ldquo;days since last completed.&amp;rdquo;&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># lib/clean_task.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">__evaluate_cleanup_timing&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">task&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Determine if task is overdue&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strptime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;updated_at&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">date_format&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">task_time&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">days&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;duration&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_todo_tasks&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Get list of overdue tasks&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">task&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">task&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tasks&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;paused&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__evaluate_cleanup_timing&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="3-notification-logic">3. Notification Logic&lt;/h3>
&lt;p>Notifications are sent only on user-specified days and times.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">should_notify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">current_time&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Determine if notification should be sent&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">notification&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;enabled&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Check day of week&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">weekday_map&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;Mon&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;Tue&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;Wed&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;Thu&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;Fri&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;Sat&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;Sun&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">current_weekday&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">weekday_map&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">current_time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">weekday&lt;/span>&lt;span class="p">()]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">current_weekday&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">notification&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;days&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Check time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">current_time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hour&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">notification&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;hour&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Check if already notified today&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="4-command-parser">4. Command Parser&lt;/h3>
&lt;p>Commands are parsed using simple string splitting.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># lib/message.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_return_message&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">s3client&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_operation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__get_task_operation_name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># First word&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__get_task_name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># Second word&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">task_operation&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;done&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">task_name&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__get_all_task_name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">clean_task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">update_task_updated_at&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task_name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">update_object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">object_keyname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">clean_task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_json&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Completed&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">task_operation&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;add&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">duration&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__get_duration&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># Third word&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">clean_task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_task&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">duration&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="5-data-structure-json">5. Data Structure (JSON)&lt;/h3>
&lt;p>One JSON file per user is stored in S3.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tasks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;task_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;vacuum&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;updated_at&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2024-01-01 12:00:00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;duration&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;paused&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;notification&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enabled&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;days&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Mon&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Wed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Fri&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;hour&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;last_notified_at&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2024-01-01 07:00:00&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="sam-template">SAM Template&lt;/h2>
&lt;p>Infrastructure is defined using AWS SAM.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># template.yaml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">AWSTemplateFormatVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2010-09-09&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">Transform&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">AWS::Serverless-2016-10-31&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">Parameters&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ChannelAccessToken&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">String&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">NoEcho&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BucketName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">String&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Default&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">your-bucket-name&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">Globals&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Function&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Runtime&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">python3.11&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Timeout&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">300&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Variables&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CHANNEL_ACCESS_TOKEN&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>!&lt;span class="l">Ref ChannelAccessToken&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BUCKET_NAME&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>!&lt;span class="l">Ref BucketName&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">Resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># Message processing Lambda&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ProcessUserMessageFunction&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">AWS::Serverless::Function&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Properties&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Handler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">line_clean_bot.process_user_message&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CodeUri&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Policies&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">S3CrudPolicy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BucketName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>!&lt;span class="l">Ref BucketName&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Events&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ApiEvent&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">HttpApi&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Properties&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/process_user_message&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Method&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ANY&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># Scheduled notification Lambda&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">PushMessagePeriodicallyFunction&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">AWS::Serverless::Function&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Properties&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Handler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">line_clean_bot.push_message_periodically&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CodeUri&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Policies&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">S3CrudPolicy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BucketName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>!&lt;span class="l">Ref BucketName&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Events&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ScheduleEvent&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Schedule&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Properties&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Schedule&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cron(0 * * * ? *) &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Every hour at minute 0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="deployment">Deployment&lt;/h2>
&lt;h3 id="1-prerequisites">1. Prerequisites&lt;/h3>
&lt;ul>
&lt;li>AWS CLI configured&lt;/li>
&lt;li>SAM CLI installed&lt;/li>
&lt;li>LINE Messaging API channel access token obtained&lt;/li>
&lt;/ul>
&lt;h3 id="2-build--deploy">2. Build &amp;amp; Deploy&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sam build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Deploy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sam deploy --parameter-overrides &lt;span class="nv">ChannelAccessToken&lt;/span>&lt;span class="o">=&lt;/span>your_token
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="3-configure-line-webhook">3. Configure LINE Webhook&lt;/h3>
&lt;p>Set the API endpoint URL output after deployment as the Webhook URL in the LINE Developers console.&lt;/p>
&lt;h2 id="local-development">Local Development&lt;/h2>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Start local API server&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sam &lt;span class="nb">local&lt;/span> start-api --env-vars env.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Run tests&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -X POST http://localhost:3000/process_user_message &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -H &lt;span class="s2">&amp;#34;Content-Type: application/json&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d @events/line_message.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="cost">Cost&lt;/h2>
&lt;p>Estimated monthly cost (assuming 100 users):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Service&lt;/th>
&lt;th>Cost&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Lambda&lt;/td>
&lt;td>Nearly free (within free tier)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>API Gateway&lt;/td>
&lt;td>Nearly free (within free tier)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>S3&lt;/td>
&lt;td>A few cents/month&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Total&lt;/strong>&lt;/td>
&lt;td>&lt;strong>A few cents to a few dollars/month&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;ul>
&lt;li>Serverless architecture minimizes operational costs&lt;/li>
&lt;li>S3 + JSON for simple data management&lt;/li>
&lt;li>SAM for infrastructure as code&lt;/li>
&lt;li>&amp;ldquo;Days elapsed&amp;rdquo; deadline management suited for cleaning tasks&lt;/li>
&lt;/ul>
&lt;p>Source code is available on &lt;a class="link" href="https://github.com/bossagyu/line-clean-bot" target="_blank" rel="noopener"
>GitHub&lt;/a>.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >How to Use the Cleaning Reminder Bot&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/002-line-messaging-api/" >LINE Messaging API Registration and Usage&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/008-aws-eventbrdge/" >How to Schedule Lambda Functions with AWS EventBridge&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/" >Integrating AWS API Gateway with Lambda&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/015-s3-object-check/" >How to Check if an S3 Object Exists in Python boto3&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use the OpenAI Response API</title><link>https://bossagyu.com/en/blog/044-openai-response-api/</link><pubDate>Wed, 24 Dec 2025 10:00:00 +0900</pubDate><guid>https://bossagyu.com/en/blog/044-openai-response-api/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article summarizes how to use the OpenAI Response API for text generation. It covers the official documentation, how to obtain an API key, and a minimal Python example.&lt;/p>
&lt;h2 id="what-is-the-response-api">What is the Response API?&lt;/h2>
&lt;p>The Response API is OpenAI&amp;rsquo;s unified API for text generation, tool calls, and multi-turn interactions. It works for both one-off requests and conversational workflows.&lt;/p>
&lt;h2 id="official-documentation">Official documentation&lt;/h2>
&lt;p>The most reliable source for the latest specs is the official documentation.&lt;/p>
&lt;ul>
&lt;li>Response API reference: &lt;a class="link" href="https://platform.openai.com/docs/api-reference/responses" target="_blank" rel="noopener"
>https://platform.openai.com/docs/api-reference/responses&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="how-to-get-an-api-key">How to get an API key&lt;/h2>
&lt;p>You need an OpenAI API key to use the Response API. Follow these steps:&lt;/p>
&lt;ol>
&lt;li>Sign in to the OpenAI dashboard&lt;/li>
&lt;li>Open the API Keys page: &lt;a class="link" href="https://platform.openai.com/api-keys" target="_blank" rel="noopener"
>https://platform.openai.com/api-keys&lt;/a>&lt;/li>
&lt;li>Create a new key with “Create new secret key”&lt;/li>
&lt;/ol>
&lt;p>Store the key in an environment variable for safety.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">OPENAI_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;YOUR_API_KEY&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="python-sample-code">Python sample code&lt;/h2>
&lt;p>Install the Python SDK with uv:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uv pip install openai
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Below is a minimal sample that calls the Response API.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">responses&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4.1-mini&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">input&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Summarize the key features of the Response API in 3 lines.&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">output_text&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="example-output">Example output&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">The Response API is a unified endpoint for one-off and conversational generation.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">It lets you retrieve model output with a simple request structure.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Start with the docs and expand as your use case grows.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>The Response API is a straightforward way to generate text with OpenAI. Check the docs, prepare an API key, and try the Python SDK for a smooth start.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/027-chatgpt-4o/" >Introduction to ChatGPT 4o&lt;/a> (overview of ChatGPT&amp;rsquo;s latest features)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/048-lawve-backend/" >Designing an Event-Driven Backend to Sync GCS with Gemini File Search API&lt;/a> (a practical example of API integration patterns)&lt;/li>
&lt;/ul></description></item><item><title>How to Use Codex CLI</title><link>https://bossagyu.com/en/blog/043-codex/</link><pubDate>Fri, 12 Dec 2025 10:00:00 +0900</pubDate><guid>https://bossagyu.com/en/blog/043-codex/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article summarizes how to access Codex from your local environment via the command line with Codex CLI. It covers installation, sign-in methods, command execution examples, and common commands.&lt;/p>
&lt;h2 id="how-to-install-codex-cli">How to Install Codex CLI&lt;/h2>
&lt;p>If you already have Node.js, you can install it via npm:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install -g codex-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If you use Homebrew, you can also install it as follows:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew tap codexcli/tap
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install codex
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After installation, run the following and confirm the version is displayed:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex --version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="how-to-sign-in">How to Sign In&lt;/h2>
&lt;p>Codex CLI requires authentication on the first run. You can sign in with either an API key or your ChatGPT account, depending on your use case.&lt;/p>
&lt;h3 id="sign-in-with-an-api-key">Sign in with an API key&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex login --token &amp;lt;YOUR_API_KEY&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You can also store it in an environment variable to avoid typing it every time:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">CODEX_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&amp;lt;YOUR_API_KEY&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="sign-in-with-your-chatgpt-account">Sign in with your ChatGPT account&lt;/h3>
&lt;p>If you prefer the browser-based flow, omit the token and run &lt;code>codex login&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex login
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This opens a browser and shows the ChatGPT sign-in screen. Sign in with your email, Google, or Apple account, grant access, and the token will be saved when you return to the CLI.&lt;/p>
&lt;p>After signing in, run the following command in either case. If your account information appears, authentication succeeded.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex whoami
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="how-to-run-codex-with-examples">How to Run Codex (with examples)&lt;/h2>
&lt;h3 id="generate-code-with-a-one-off-prompt">Generate code with a one-off prompt&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex run &lt;span class="s2">&amp;#34;Write FizzBuzz in Python&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="summarize-while-attaching-a-file-as-context">Summarize while attaching a file as context&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex run &lt;span class="s2">&amp;#34;Summarize this file&amp;#34;&lt;/span> --file README.md
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="try-interactive-chat-mode">Try interactive chat mode&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex chat
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In chat mode, you can enter prompts to receive responses in sequence. Press &lt;code>Ctrl + D&lt;/code> to exit.&lt;/p>
&lt;h4 id="slash-commands-in-chat">Slash commands in chat&lt;/h4>
&lt;p>In chat mode, type a &lt;code>/&lt;/code> prefix to perform helper actions.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Command&lt;/th>
&lt;th>Purpose&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>/help&lt;/code>&lt;/td>
&lt;td>Display available commands&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/new&lt;/code>&lt;/td>
&lt;td>Reset the conversation history and start a new thread&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/file &amp;lt;path&amp;gt;&lt;/code>&lt;/td>
&lt;td>Attach the specified file as context&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/exit&lt;/code>&lt;/td>
&lt;td>Exit chat&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="how-to-use-the-commands">How to Use the Commands&lt;/h2>
&lt;p>Here are the main commands:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Command&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>codex login --token &amp;lt;API_KEY&amp;gt;&lt;/code>&lt;/td>
&lt;td>Sign in with an API key&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex login&lt;/code>&lt;/td>
&lt;td>Start browser authentication with your ChatGPT account&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex whoami&lt;/code>&lt;/td>
&lt;td>Check the currently signed-in account&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex run &amp;quot;&amp;lt;prompt&amp;gt;&amp;quot;&lt;/code>&lt;/td>
&lt;td>Send a one-off prompt and get the result&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex run &amp;quot;&amp;lt;prompt&amp;gt;&amp;quot; --file &amp;lt;path&amp;gt;&lt;/code>&lt;/td>
&lt;td>Run with a file included as context&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex chat&lt;/code>&lt;/td>
&lt;td>Use interactive chat mode&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex history&lt;/code>&lt;/td>
&lt;td>Review recent execution history&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>Once installed and signed in, you can immediately start using Codex from the command line. Knowing the frequently used commands helps you quickly generate scripts or summarize files.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/017-vscode-copilot/" >Complete Guide to Using GitHub Copilot in VSCode&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/042-github-copilot/" >How to Use GitHub Copilot More Effectively with Chat Tools&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/044-openai-response-api/" >How to Use the OpenAI Response API&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use GitHub Copilot More Effectively with Chat Tools</title><link>https://bossagyu.com/en/blog/042-github-copilot/</link><pubDate>Tue, 07 Oct 2025 08:39:41 +0900</pubDate><guid>https://bossagyu.com/en/blog/042-github-copilot/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to use Chat Tools to make GitHub Copilot Chat even more powerful.
By using Chat Tools, you can perform various tasks directly within your conversation with Copilot.&lt;/p>
&lt;h2 id="what-are-chat-tools">What Are Chat Tools?&lt;/h2>
&lt;p>Chat Tools are features within GitHub Copilot Chat that allow you to execute special tasks.
For example, you can fetch terminal outputs or retrieve information from URLs—all from within the chat interface.&lt;/p>
&lt;h2 id="how-to-use-chat-tools">How to Use Chat Tools&lt;/h2>
&lt;p>Here’s how to use Chat Tools in VSCode.&lt;/p>
&lt;p>Basically, you can execute them by typing a command in the following format:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">#&amp;lt;command&amp;gt; &amp;lt;args&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;img src="https://bossagyu.com/en/blog/042-github-copilot/image.png"
width="940"
height="119"
srcset="https://bossagyu.com/en/blog/042-github-copilot/image_hu36d6687e3f7736497a6806aaa78e3847_23791_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/042-github-copilot/image_hu36d6687e3f7736497a6806aaa78e3847_23791_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Example of running a Chat Tool"
class="gallery-image"
data-flex-grow="789"
data-flex-basis="1895px"
>&lt;/p>
&lt;p>That’s all there is to it.&lt;/p>
&lt;h2 id="common-chat-tool-commands">Common Chat Tool Commands&lt;/h2>
&lt;p>Below are some of the most useful Chat Tool commands:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Command&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>#codebase&lt;/code>&lt;/td>
&lt;td>Searches the entire current workspace.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>#selection&lt;/code>&lt;/td>
&lt;td>Adds the currently selected code in the editor as context to the prompt.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>#terminal_selection&lt;/code>&lt;/td>
&lt;td>Adds the selected output in the terminal as context to the prompt.&lt;br>Useful when referencing terminal output after an error occurs.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>#fetch_webpage&lt;/code>&lt;/td>
&lt;td>Retrieves content from a specified URL and adds it as context to the prompt.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>There are many more useful commands available—refer to the official documentation for details:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://code.visualstudio.com/docs/copilot/reference/copilot-vscode-features#_chat-tools" target="_blank" rel="noopener"
>GitHub Copilot Chat Tools&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>In this article, we covered how to use Chat Tools within GitHub Copilot Chat.
By taking advantage of these tools, you can make Copilot even more convenient and powerful—so give them a try!&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/017-vscode-copilot/" >Complete Guide to Using GitHub Copilot in VSCode&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/035-github-copilot-markdown/" >How to Enable GitHub Copilot for Markdown&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/043-codex/" >How to Use Codex CLI&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use Redis with Go</title><link>https://bossagyu.com/en/blog/041-redis-go/</link><pubDate>Sun, 05 Oct 2025 20:25:02 +0900</pubDate><guid>https://bossagyu.com/en/blog/041-redis-go/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to use Redis with Go.&lt;/p>
&lt;h2 id="starting-redis">Starting Redis&lt;/h2>
&lt;p>For an explanation of Redis and how to start it using Docker, refer to &lt;a class="link" href="https://bossagyu.com/blog/040-redis-local/" target="_blank" rel="noopener"
>this article&lt;/a>.&lt;/p>
&lt;p>Start Redis:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -d --name my-redis &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -p 6379:6379 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> redis:7.4.5-alpine
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Check the connection using &lt;code>redis-cli&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">docker exec -it my-redis redis-cli ping
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If it returns &lt;code>PONG&lt;/code>, the connection is successful.&lt;/p>
&lt;h2 id="create-a-go-project">Create a Go Project&lt;/h2>
&lt;p>Create a new Go project:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir redis-go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> redis-go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">go mod init redis-go
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="using-redis-in-go">Using Redis in Go&lt;/h2>
&lt;p>Here is a sample code that connects to Redis, writes, and reads data:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">package&lt;/span> &lt;span class="nx">main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;context&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;log&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;time&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;github.com/redis/go-redis/v9&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Connect to local Redis on port 6379 without a password
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">rdb&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">redis&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">redis&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Options&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Addr&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s">&amp;#34;127.0.0.1:6379&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Password&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// no password
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">DB&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">DialTimeout&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ReadTimeout&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">WriteTimeout&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">cancel&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WithTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Background&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nf">cancel&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Ping to test connection
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Ping&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Err&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;redis ping failed: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;connected to redis ✅&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Basic operations (SET/GET)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;world&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Minute&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Err&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;SET error: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">val&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Result&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;GET error: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;hello =&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">val&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Example using Set type
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">key&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="s">&amp;#34;team:dev&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;alice&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;bob&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;alice&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Err&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;SADD error: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">members&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">_&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SMembers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">key&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Result&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;members =&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">members&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install the dependencies:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">go mod tidy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Run the program:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">go run main.go
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If you see the following output, everything is working correctly:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">2025/10/05 20:30:00 connected to redis ✅
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hello = world
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">members = [alice bob]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article explained how to use Redis with Go.
When working with Redis in Go, use the &lt;a class="link" href="https://github.com/redis/go-redis" target="_blank" rel="noopener"
>go-redis&lt;/a> package.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/040-redis-local/" >How to Run Redis with Docker&lt;/a> (how to start Redis)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/030-go-environment-construction/" >Setting Up a Go Development Environment on Mac and Running Hello World&lt;/a> (Go environment setup)&lt;/li>
&lt;/ul></description></item><item><title>How to Run Redis with Docker</title><link>https://bossagyu.com/en/blog/040-redis-local/</link><pubDate>Fri, 03 Oct 2025 07:29:31 +0900</pubDate><guid>https://bossagyu.com/en/blog/040-redis-local/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to run Redis with Docker.&lt;/p>
&lt;h2 id="what-is-redis">What is Redis?&lt;/h2>
&lt;p>Redis is an open-source in-memory data structure store that can be used as a database, cache, and message broker. It provides high-speed read/write performance and supports various data structures such as strings, lists, sets, and hashes.&lt;/p>
&lt;h2 id="running-redis-with-docker">Running Redis with Docker&lt;/h2>
&lt;h3 id="1-install-docker">1. Install Docker&lt;/h3>
&lt;p>First, make sure Docker is installed.
If it is not installed, please download and install it from the &lt;a class="link" href="https://www.docker.com/get-started" target="_blank" rel="noopener"
>official Docker website&lt;/a>.&lt;/p>
&lt;h3 id="2-start-a-redis-container">2. Start a Redis Container&lt;/h3>
&lt;p>Run the following command to start a Redis container:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --name redis -d -p 6379:6379 redis:7.4.5-alpine
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Log in to the container:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> -it redis sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="3-verify-redis-operation">3. Verify Redis Operation&lt;/h3>
&lt;p>Inside the Redis container, run the following command to start the Redis client:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">redis-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Try setting and getting data:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">set&lt;/span> name &lt;span class="s2">&amp;#34;hoge&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">get name
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;hoge&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>At this point, you can confirm that Redis is working correctly.&lt;/p>
&lt;h3 id="4-stop-and-remove-the-redis-container">4. Stop and Remove the Redis Container&lt;/h3>
&lt;p>To stop the Redis container, run:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker stop redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>To remove the Redis container, run:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker rm redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>In this article, we explained how to run Redis using Docker.
Redis is widely used as a high-performance database, and with Docker, it can be easily set up.
Give Redis a try and see how it can help you.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/041-redis-go/" >How to Use Redis with Go&lt;/a> (practical example of using Redis in Go)&lt;/li>
&lt;/ul></description></item><item><title>How to Use AWS Toolkit in Visual Studio Code</title><link>https://bossagyu.com/en/blog/039-aws-toolkit-vscode/</link><pubDate>Mon, 11 Aug 2025 20:33:30 +0900</pubDate><guid>https://bossagyu.com/en/blog/039-aws-toolkit-vscode/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to use AWS Toolkit in Visual Studio Code (VSCode).&lt;/p>
&lt;h2 id="installing-aws-toolkit">Installing AWS Toolkit&lt;/h2>
&lt;ol>
&lt;li>Launch VSCode.&lt;/li>
&lt;li>Click the Extensions icon in the sidebar on the left.&lt;/li>
&lt;li>Type &amp;ldquo;AWS Toolkit&amp;rdquo; in the search bar and select &amp;ldquo;AWS Toolkit for Visual Studio Code&amp;rdquo; from the list.&lt;/li>
&lt;li>Click the &amp;ldquo;Install&amp;rdquo; button to start the installation.&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/039-aws-toolkit-vscode/image.png"
width="1192"
height="265"
srcset="https://bossagyu.com/en/blog/039-aws-toolkit-vscode/image_hufa1fd2602d4a5c45942f480dd2c9a493_78579_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/039-aws-toolkit-vscode/image_hufa1fd2602d4a5c45942f480dd2c9a493_78579_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWS Toolkit Installation"
class="gallery-image"
data-flex-grow="449"
data-flex-basis="1079px"
>&lt;/p>
&lt;h2 id="how-to-set-the-asia-pacific-tokyo-region">How to Set the Asia Pacific Tokyo Region&lt;/h2>
&lt;p>By default, the region is set to us-east-1, so you need to change it to Asia Pacific Tokyo.&lt;br>
The procedure is a bit tricky, so follow the steps below:&lt;/p>
&lt;ol>
&lt;li>In the left sidebar, open &amp;ldquo;AWS Explorer&amp;rdquo; and click the hamburger menu.&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/039-aws-toolkit-vscode/image-1.png"
width="658"
height="259"
srcset="https://bossagyu.com/en/blog/039-aws-toolkit-vscode/image-1_hu4dbc19d229257ea92a49e677c002202d_66951_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/039-aws-toolkit-vscode/image-1_hu4dbc19d229257ea92a49e677c002202d_66951_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWS Explorer"
class="gallery-image"
data-flex-grow="254"
data-flex-basis="609px"
>&lt;/p>
&lt;ol start="2">
&lt;li>Click &lt;code>Show or Hide Regions&lt;/code>.&lt;/li>
&lt;li>From the displayed list, select &amp;ldquo;Asia Pacific (Tokyo) ap-northeast-1&amp;rdquo;.&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/039-aws-toolkit-vscode/image-2.png"
width="600"
height="211"
srcset="https://bossagyu.com/en/blog/039-aws-toolkit-vscode/image-2_hu8226c6e3ff7c2fce4d95162999eea97c_27977_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/039-aws-toolkit-vscode/image-2_hu8226c6e3ff7c2fce4d95162999eea97c_27977_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWS Explorer"
class="gallery-image"
data-flex-grow="284"
data-flex-basis="682px"
>&lt;/p>
&lt;p>Now, you can use AWS Toolkit in the Asia Pacific Tokyo region.&lt;/p></description></item><item><title>How to Update Visual Studio Code</title><link>https://bossagyu.com/en/blog/038-update-vscode/</link><pubDate>Sun, 27 Jul 2025 18:12:16 +0900</pubDate><guid>https://bossagyu.com/en/blog/038-update-vscode/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to update Visual Studio Code (VSCode) on Mac.
In addition to manual GUI updates, it also covers command-line updates and auto-update settings.&lt;/p>
&lt;h2 id="updating-via-gui">Updating via GUI&lt;/h2>
&lt;ol>
&lt;li>Launch VSCode.&lt;/li>
&lt;li>Go to Code → &amp;ldquo;Check for Updates&amp;rdquo;.&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/038-update-vscode/image.png"
width="362"
height="266"
srcset="https://bossagyu.com/en/blog/038-update-vscode/image_hu4cb88fc77c0541cdf7c5ee55b378b842_31419_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/038-update-vscode/image_hu4cb88fc77c0541cdf7c5ee55b378b842_31419_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Check for Updates"
class="gallery-image"
data-flex-grow="136"
data-flex-basis="326px"
>&lt;/p>
&lt;p>When the update starts, you will see a screen like this:&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/038-update-vscode/image-1.png"
width="322"
height="260"
srcset="https://bossagyu.com/en/blog/038-update-vscode/image-1_hu467c1c2e48f82f20717d31d719e9ad63_31541_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/038-update-vscode/image-1_hu467c1c2e48f82f20717d31d719e9ad63_31541_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Downloading update"
class="gallery-image"
data-flex-grow="123"
data-flex-basis="297px"
>&lt;/p>
&lt;p>Once the download is complete, &amp;ldquo;Restart to Update&amp;rdquo; will be displayed.&lt;/p>
&lt;p>After restarting, click &amp;ldquo;About Visual Studio Code&amp;rdquo; to confirm that the version has been updated.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/038-update-vscode/image-3.png"
width="243"
height="338"
srcset="https://bossagyu.com/en/blog/038-update-vscode/image-3_hud4f1281fbbaa937e53ab24c19c8a91ab_31593_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/038-update-vscode/image-3_hud4f1281fbbaa937e53ab24c19c8a91ab_31593_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Version confirmation screen"
class="gallery-image"
data-flex-grow="71"
data-flex-basis="172px"
>&lt;/p>
&lt;h2 id="updating-via-command-line">Updating via Command Line&lt;/h2>
&lt;p>If you installed VSCode via Homebrew, you can update it from the terminal.&lt;/p>
&lt;h3 id="check-the-current-version">Check the current version&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">code --version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="update-with-homebrew">Update with Homebrew&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew upgrade --cask visual-studio-code
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If VSCode was not installed via Homebrew, first remove the existing VSCode from your Applications folder, then install it via Homebrew.
Your settings and extensions can be restored via account sync, so removing it is safe.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew install --cask visual-studio-code
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After this, you can use &lt;code>brew upgrade --cask visual-studio-code&lt;/code> for future updates.&lt;/p>
&lt;h2 id="auto-update-settings">Auto-Update Settings&lt;/h2>
&lt;p>VSCode has an &lt;code>update.mode&lt;/code> setting that controls update behavior.
Open Settings (&lt;code>Cmd + ,&lt;/code>) and search for &lt;code>update mode&lt;/code> to change it.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Value&lt;/th>
&lt;th>Behavior&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>default&lt;/code>&lt;/td>
&lt;td>Automatically downloads and installs updates in the background&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>manual&lt;/code>&lt;/td>
&lt;td>Checks for updates but requires manual installation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>start&lt;/code>&lt;/td>
&lt;td>Only checks for updates when VSCode starts&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>none&lt;/code>&lt;/td>
&lt;td>Completely disables automatic updates&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>To configure directly in settings.json:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;update.mode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;default&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Unless you have a specific reason, &lt;code>default&lt;/code> is the recommended setting.&lt;/p>
&lt;h2 id="troubleshooting">Troubleshooting&lt;/h2>
&lt;h3 id="check-for-updates-shows-no-updates-available">&amp;ldquo;Check for Updates&amp;rdquo; shows no updates available&lt;/h3>
&lt;p>Auto-update may be disabled. Check that &lt;code>update.mode&lt;/code> is not set to &lt;code>none&lt;/code> in your settings.&lt;/p>
&lt;h3 id="update-download-fails">Update download fails&lt;/h3>
&lt;p>This may be caused by proxy or firewall settings. Check the following configuration:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;http.proxy&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;http://proxy.example.com:8080&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;http.proxyStrictSSL&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="extensions-stop-working-after-update">Extensions stop working after update&lt;/h3>
&lt;p>This may be due to extension compatibility issues. Try the following steps:&lt;/p>
&lt;ol>
&lt;li>Open the Command Palette (&lt;code>Cmd + Shift + P&lt;/code>)&lt;/li>
&lt;li>Run &amp;ldquo;Extensions: Disable All Installed Extensions&amp;rdquo;&lt;/li>
&lt;li>Restart VSCode&lt;/li>
&lt;li>Re-enable extensions one by one to identify the problematic one&lt;/li>
&lt;/ol>
&lt;h3 id="if-the-issue-persists">If the issue persists&lt;/h3>
&lt;p>Reinstalling VSCode may resolve the problem.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If installed via Homebrew&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew reinstall --cask visual-studio-code
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If you sync your settings via a GitHub or Microsoft account, your configuration and installed extensions will be automatically restored after reinstallation.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article covered how to update VSCode.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>GUI&lt;/strong>: Code → &amp;ldquo;Check for Updates&amp;rdquo; — a few clicks to complete&lt;/li>
&lt;li>&lt;strong>Command line&lt;/strong>: &lt;code>brew upgrade --cask visual-studio-code&lt;/code> for quick updates&lt;/li>
&lt;li>&lt;strong>Auto-update&lt;/strong>: Control behavior with the &lt;code>update.mode&lt;/code> setting&lt;/li>
&lt;/ul>
&lt;p>By updating regularly, you can take advantage of new features and security patches.
After updating, always check the version information to confirm the update was successful.&lt;/p></description></item><item><title>How to Set Up Gemini CLI on Mac</title><link>https://bossagyu.com/en/blog/037-gemini-cli/</link><pubDate>Tue, 22 Jul 2025 23:21:19 +0900</pubDate><guid>https://bossagyu.com/en/blog/037-gemini-cli/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to set up Gemini CLI.&lt;/p>
&lt;h2 id="how-to-set-up-gemini-cli">How to Set Up Gemini CLI&lt;/h2>
&lt;h3 id="1-install-gemini-cli">1. Install Gemini CLI&lt;/h3>
&lt;p>Use Homebrew to install Gemini CLI.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew install gemini-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-configure-gemini-cli">2. Configure Gemini CLI&lt;/h3>
&lt;p>Run the &lt;code>gemini&lt;/code> command.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">gemini
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;img src="https://bossagyu.com/en/blog/037-gemini-cli/image.png"
width="1306"
height="870"
srcset="https://bossagyu.com/en/blog/037-gemini-cli/image_hu0be541ce16dda5305ce2cda0e13985ce_167999_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/037-gemini-cli/image_hu0be541ce16dda5305ce2cda0e13985ce_167999_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Gemini Startup Screen"
class="gallery-image"
data-flex-grow="150"
data-flex-basis="360px"
>&lt;/p>
&lt;p>You can choose to log in or enter an API key to use it.&lt;br>
This time, we will select &amp;ldquo;Login with Google&amp;rdquo; to log in.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/037-gemini-cli/image-1.png"
width="1224"
height="572"
srcset="https://bossagyu.com/en/blog/037-gemini-cli/image-1_hudec8bce04a90cc5853f77858dc3fe150_85544_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/037-gemini-cli/image-1_hudec8bce04a90cc5853f77858dc3fe150_85544_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Gemini Selection Screen"
class="gallery-image"
data-flex-grow="213"
data-flex-basis="513px"
>&lt;/p>
&lt;p>The screen transitions to the login page.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/image-2.png"
loading="lazy"
alt="Login Screen"
>&lt;/p>
&lt;p>Once logged in, Gemini CLI becomes available.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/037-gemini-cli/image-3.png"
width="1236"
height="514"
srcset="https://bossagyu.com/en/blog/037-gemini-cli/image-3_hu7826108cbaae69c4267c397afc695858_101200_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/037-gemini-cli/image-3_hu7826108cbaae69c4267c397afc695858_101200_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Gemini Terminal"
class="gallery-image"
data-flex-grow="240"
data-flex-basis="577px"
>&lt;/p>
&lt;h3 id="3-how-to-use-gemini-cli">3. How to Use Gemini CLI&lt;/h3>
&lt;p>You can ask questions or request tasks using Gemini CLI.&lt;/p>
&lt;p>This time, we will ask it to solve the FizzBuzz problem.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">gemini
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Type &amp;ldquo;Please write the code for FizzBuzz&amp;rdquo; into the Gemini CLI prompt.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/037-gemini-cli/image-4.png"
width="1528"
height="1054"
srcset="https://bossagyu.com/en/blog/037-gemini-cli/image-4_hu27c9819eb3a319aacaaf80a7a560f69d_462615_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/037-gemini-cli/image-4_hu27c9819eb3a319aacaaf80a7a560f69d_462615_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Gemini CLI Prompt"
class="gallery-image"
data-flex-grow="144"
data-flex-basis="347px"
>&lt;/p>
&lt;p>After inputting, Gemini will generate the code for you.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/037-gemini-cli/image-5.png"
width="1166"
height="842"
srcset="https://bossagyu.com/en/blog/037-gemini-cli/image-5_hu3abc355b9f0f59267401931abc93d618_167247_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/037-gemini-cli/image-5_hu3abc355b9f0f59267401931abc93d618_167247_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Gemini CLI Execution Result"
class="gallery-image"
data-flex-grow="138"
data-flex-basis="332px"
>&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>By setting up Gemini CLI, you can use Gemini AI directly from the terminal.
This enables efficient development and task management using AI.
Gemini CLI is very user-friendly and makes terminal work smoother.
Give it a try!&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/027-chatgpt-4o/" >Introduction to ChatGPT 4o&lt;/a> (comparison with ChatGPT)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/044-openai-response-api/" >How to Use the OpenAI Response API&lt;/a> (API integration in practice)&lt;/li>
&lt;/ul></description></item><item><title>How to Customize Indent Guides in VSCode</title><link>https://bossagyu.com/en/blog/036-vscode-indent/</link><pubDate>Tue, 22 Jul 2025 21:01:13 +0900</pubDate><guid>https://bossagyu.com/en/blog/036-vscode-indent/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>In Visual Studio Code (VSCode), you can customize the colors of indent guides.&lt;br>
This article explains how to change the colors of indent guides.&lt;br>
While extensions like indent-rainbow often have issues such as guides being cut off, you can achieve this simply by editing the VSCode settings file.&lt;/p>
&lt;h2 id="how-to-change-the-colors-of-indent-guides">How to Change the Colors of Indent Guides&lt;/h2>
&lt;h3 id="open-the-settings-file">Open the Settings File&lt;/h3>
&lt;p>Open the VSCode settings file.&lt;br>
Open the &lt;code>Command Palette&lt;/code> (Ctrl + Shift + P) and select &lt;code>Preferences: Open Settings (JSON)&lt;/code>.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/036-vscode-indent/image.png"
width="608"
height="122"
srcset="https://bossagyu.com/en/blog/036-vscode-indent/image_hu3c9579035f233532becca6f8faa5c647_25704_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/036-vscode-indent/image_hu3c9579035f233532becca6f8faa5c647_25704_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="How to open settings"
class="gallery-image"
data-flex-grow="498"
data-flex-basis="1196px"
>&lt;/p>
&lt;h3 id="add-settings">Add Settings&lt;/h3>
&lt;p>Add the following settings as a reference:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;workbench.colorCustomizations&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background1&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#006400&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background2&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#008000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background3&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00a000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background4&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#006400&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background5&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#008000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background6&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00a000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground1&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground2&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground3&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground4&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground5&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground6&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorWhitespace.foreground&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#393A3D&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Setting&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>editorIndentGuide.background&amp;lt;number&amp;gt;&lt;/code>&lt;/td>
&lt;td>Specifies the background color of the indent guide.&lt;br>&lt;code>&amp;lt;number&amp;gt;&lt;/code> corresponds to the depth of the indent.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>editorIndentGuide.activeBackground&amp;lt;number&amp;gt;&lt;/code>&lt;/td>
&lt;td>Specifies the background color of the active indent guide.&lt;br>&lt;code>&amp;lt;number&amp;gt;&lt;/code> corresponds to the depth of the indent.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>editorWhitespace.foreground&lt;/code>&lt;/td>
&lt;td>Specifies the color of whitespace characters.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>After adding the settings, the colors of the indent guides will be changed as shown in the image below.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/036-vscode-indent/image-1.png"
width="479"
height="328"
srcset="https://bossagyu.com/en/blog/036-vscode-indent/image-1_hu6f9c993cad77886be734ef5704270561_75507_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/036-vscode-indent/image-1_hu6f9c993cad77886be734ef5704270561_75507_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Indent guide appearance"
class="gallery-image"
data-flex-grow="146"
data-flex-basis="350px"
>&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>In VSCode, you can easily customize the colors of indent guides.&lt;br>
By simply adding the necessary items to the settings file, you can change them to your preferred colors.&lt;br>
This improves code readability and makes it easier to visually grasp the depth of indents.&lt;br>
Give it a try!&lt;/p></description></item><item><title>How to Enable GitHub Copilot for Markdown</title><link>https://bossagyu.com/en/blog/035-github-copilot-markdown/</link><pubDate>Mon, 21 Jul 2025 10:00:00 +0900</pubDate><guid>https://bossagyu.com/en/blog/035-github-copilot-markdown/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>By default, GitHub Copilot is disabled for Markdown files.&lt;br>
This article explains how to enable GitHub Copilot for Markdown.&lt;/p>
&lt;h2 id="configuring-github-copilot">Configuring GitHub Copilot&lt;/h2>
&lt;p>Open the GitHub Copilot plugin page in VSCode.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/035-github-copilot-markdown/image.png"
width="1032"
height="485"
srcset="https://bossagyu.com/en/blog/035-github-copilot-markdown/image_hu4f0b967461f0cc9ec012b506a04e858f_139974_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/035-github-copilot-markdown/image_hu4f0b967461f0cc9ec012b506a04e858f_139974_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Copilot Settings Screen"
class="gallery-image"
data-flex-grow="212"
data-flex-basis="510px"
>&lt;/p>
&lt;p>Click the gear icon to open the settings.&lt;/p>
&lt;p>In the settings screen, change &lt;code>markdown&lt;/code> from &lt;code>false&lt;/code> to &lt;code>true&lt;/code>.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/035-github-copilot-markdown/image-1.png"
width="817"
height="298"
srcset="https://bossagyu.com/en/blog/035-github-copilot-markdown/image-1_hua6c26bb551050949dee2247188038d10_37461_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/035-github-copilot-markdown/image-1_hua6c26bb551050949dee2247188038d10_37461_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Enable Markdown Screen"
class="gallery-image"
data-flex-grow="274"
data-flex-basis="657px"
>&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>Enabling GitHub Copilot for Markdown makes editing Markdown files more efficient.
You can enable it easily by changing the settings in VSCode, so give it a try!&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/017-vscode-copilot/" >Complete Guide to Using GitHub Copilot in VSCode&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/042-github-copilot/" >How to Use GitHub Copilot More Effectively with Chat Tools&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Upgrade JetBrains Products to a Major Version</title><link>https://bossagyu.com/en/blog/034-jetbrains-update/</link><pubDate>Thu, 12 Jun 2025 20:46:53 +0900</pubDate><guid>https://bossagyu.com/en/blog/034-jetbrains-update/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>When upgrading major versions of JetBrains products such as IntelliJ or PyCharm, you need to manually download the new version.&lt;br>
This article explains how to update easily while preserving your settings.&lt;/p>
&lt;h2 id="installing-the-jetbrains-toolbox-app">Installing the JetBrains Toolbox App&lt;/h2>
&lt;p>When upgrading to a major version of a JetBrains product, it is recommended to use the &lt;a class="link" href="https://www.jetbrains.com/toolbox-app/" target="_blank" rel="noopener"
>JetBrains Toolbox App&lt;/a>.&lt;/p>
&lt;p>Download and install the JetBrains Toolbox App from the link above.&lt;/p>
&lt;h2 id="update">Update&lt;/h2>
&lt;p>Launch the JetBrains Toolbox App and select the product you want to update.&lt;/p>
&lt;p>In this example, we will update PyCharm. Click on it to start the update.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/034-jetbrains-update/img.png"
width="443"
height="691"
srcset="https://bossagyu.com/en/blog/034-jetbrains-update/img_hu942b99710855521c5f32d9b20e79451d_198115_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/034-jetbrains-update/img_hu942b99710855521c5f32d9b20e79451d_198115_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Update selection screen"
class="gallery-image"
data-flex-grow="64"
data-flex-basis="153px"
>&lt;/p>
&lt;p>Once the update is complete, the new version will be installed as shown below.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/034-jetbrains-update/img_1.png"
width="423"
height="509"
srcset="https://bossagyu.com/en/blog/034-jetbrains-update/img_1_hu8eb35234db364754b9c1db3a37ad674a_94027_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/034-jetbrains-update/img_1_hu8eb35234db364754b9c1db3a37ad674a_94027_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Screen after update"
class="gallery-image"
data-flex-grow="83"
data-flex-basis="199px"
>&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>When upgrading to a major version of a JetBrains product, using the JetBrains Toolbox App makes the update process easy.&lt;/p></description></item><item><title>Running the Feast Tutorial on macOS</title><link>https://bossagyu.com/en/blog/033-feast-tutorial/</link><pubDate>Mon, 13 Jan 2025 11:49:53 +0900</pubDate><guid>https://bossagyu.com/en/blog/033-feast-tutorial/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>In this article, we’ll follow the &lt;a class="link" href="https://github.com/feast-dev/feast" target="_blank" rel="noopener"
>Feast tutorial&lt;/a> and run it on a Mac.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>Refer to &lt;a class="link" href="https://bossagyu.com/blog/032-python-uv/" target="_blank" rel="noopener"
>Setting up a Python development environment on Mac with UV&lt;/a> to prepare your environment. Once you’ve set up UV, install Feast in that environment.&lt;/p>
&lt;h2 id="installing-feast-and-launching-the-ui">Installing Feast and Launching the UI&lt;/h2>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">uv pip install &lt;span class="nv">feast&lt;/span>&lt;span class="o">==&lt;/span>0.40.1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>Note&lt;/strong>&lt;br>
As of 2025/01/13, there is a known bug in Feast causing the UI to fail on the latest version. (&lt;a class="link" href="https://github.com/feast-dev/feast/issues/4743" target="_blank" rel="noopener"
>Issue&lt;/a>)&lt;/p>
&lt;/blockquote>
&lt;p>Initialize a Feature Repository:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">feast init my_feature_repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Creating a new Feast repository in /Users/kouhei/Program/ML/feast/my_feature_repo.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You’ll see a new repository like this:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">tree my_feature_repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── my_feature_repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── feature_repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ └── driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── example_repo.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── feature_store.yaml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── test_workflow.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Apply the repository configuration:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my_feature_repo/feature_repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">feast apply
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Then launch the Feast UI:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">feast ui
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Access &lt;code>http://0.0.0.0:8888/p/my_feature_repo&lt;/code> to see the interface.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/033-feast-tutorial/img-033-001.png"
width="1478"
height="581"
srcset="https://bossagyu.com/en/blog/033-feast-tutorial/img-033-001_hub81249ce2a094e96f7c08a17dccf8b99_125412_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/033-feast-tutorial/img-033-001_hub81249ce2a094e96f7c08a17dccf8b99_125412_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Feast UI"
class="gallery-image"
data-flex-grow="254"
data-flex-basis="610px"
>&lt;/p>
&lt;h2 id="manipulating-data-in-feast">Manipulating Data in Feast&lt;/h2>
&lt;h3 id="1-creating-a-training-dataset">1. Creating a Training Dataset&lt;/h3>
&lt;p>Starting from step 5 in the tutorial, you’ll use Jupyter Notebook. Install it first:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">uv pip install jupyter
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Launch the notebook:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">jupyter notebook
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In a Jupyter Notebook, run the following code to create a dataset for training:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">feast&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FeatureStore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pandas&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">pd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datetime&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">datetime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">entity_df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pd&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">DataFrame&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_dict&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;driver_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1001&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1002&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1003&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1004&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;event_timestamp&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">datetime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2021&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">59&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">42&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">datetime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2021&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">datetime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2021&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">26&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">datetime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2021&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">15&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FeatureStore&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">repo_path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">training_df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">store&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_historical_features&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entity_df&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">entity_df&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">features&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:conv_rate&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:acc_rate&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:avg_daily_trips&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">to_df&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">training_df&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">head&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Train model&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># model = ml.fit(training_df)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Below is an example of the notebook output:&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/033-feast-tutorial/img-033-002.png"
width="1258"
height="893"
srcset="https://bossagyu.com/en/blog/033-feast-tutorial/img-033-002_hu9a63a8af0e8adbd21872e8fe4c7e159c_152409_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/033-feast-tutorial/img-033-002_hu9a63a8af0e8adbd21872e8fe4c7e159c_152409_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Training Data in Feast"
class="gallery-image"
data-flex-grow="140"
data-flex-basis="338px"
>&lt;/p>
&lt;h3 id="2-materializing-the-online-store">2. Materializing the Online Store&lt;/h3>
&lt;p>To populate the Online Store, run:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">feast materialize 1970-01-01T00:00:00Z 2025-01-04T01:24:24Z
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>Note&lt;/strong>&lt;br>
In the provided sample, the command &lt;code>feast materialize-incremental $CURRENT_TIME&lt;/code> may not work, so we specify a broader time range here.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">01/04/2025 10:28:40 AM root WARNING: _list_feature_views will make breaking changes. ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Materializing &lt;span class="m">2&lt;/span> feature views from 1970-01-01 09:00:00+09:00 to 2025-01-04 10:24:24+09:00 into the sqlite online store.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">driver_hourly_stats_fresh:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0%&lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span> 0/5 ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">100%&lt;span class="p">|&lt;/span>███████████████████████████████████████████████████████████████&lt;span class="p">|&lt;/span> 5/5 ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">driver_hourly_stats:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">100%&lt;span class="p">|&lt;/span>███████████████████████████████████████████████████████████████&lt;span class="p">|&lt;/span> 5/5 ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="3-retrieving-data-from-the-online-store">3. Retrieving Data from the Online Store&lt;/h3>
&lt;p>Use Jupyter Notebook to fetch data from the Online Store:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pprint&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">pprint&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">feast&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FeatureStore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FeatureStore&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">repo_path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">feature_vector&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">store&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_online_features&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">features&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:conv_rate&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:acc_rate&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:avg_daily_trips&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entity_rows&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;driver_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1001&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">to_dict&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">pprint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">feature_vector&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Make prediction&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># model.predict(feature_vector)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Example output:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="o">{&lt;/span>&lt;span class="s1">&amp;#39;acc_rate&amp;#39;&lt;/span>: &lt;span class="o">[&lt;/span>0.5004482269287109&lt;span class="o">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;avg_daily_trips&amp;#39;&lt;/span>: &lt;span class="o">[&lt;/span>691&lt;span class="o">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;conv_rate&amp;#39;&lt;/span>: &lt;span class="o">[&lt;/span>0.3067885637283325&lt;span class="o">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_id&amp;#39;&lt;/span>: &lt;span class="o">[&lt;/span>1001&lt;span class="o">]}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;img src="https://bossagyu.com/en/blog/033-feast-tutorial/img-033-002.png"
width="1258"
height="893"
srcset="https://bossagyu.com/en/blog/033-feast-tutorial/img-033-002_hu9a63a8af0e8adbd21872e8fe4c7e159c_152409_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/033-feast-tutorial/img-033-002_hu9a63a8af0e8adbd21872e8fe4c7e159c_152409_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Retrieving Data from Feast"
class="gallery-image"
data-flex-grow="140"
data-flex-basis="338px"
>&lt;/p>
&lt;p>We’ve successfully materialized data in the Online Store and fetched it using Feast.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>We followed the Feast tutorial to prepare training data, materialize that data into the Online Store, and retrieve it. By managing both training and inference datasets in Feast, you can avoid training-serving skew, enhancing the consistency of your ML workflows.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/032-python-uv/" >Setting Up a Python Development Environment on Mac with UV&lt;/a> (Python environment setup)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/004-python-setup/" >Setting Up a Local Environment Using Pyenv and venv&lt;/a> (traditional Python environment setup)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/031-read-parquet-file/" >How to Read Parquet Files on macOS&lt;/a> (Feast sample data format)&lt;/li>
&lt;/ul></description></item><item><title>Setting Up a Python Development Environment on Mac with UV</title><link>https://bossagyu.com/en/blog/032-python-uv/</link><pubDate>Wed, 01 Jan 2025 14:39:53 +0900</pubDate><guid>https://bossagyu.com/en/blog/032-python-uv/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article introduces how to set up a Python development environment on Mac using UV.&lt;/p>
&lt;h2 id="what-is-uv">What is UV?&lt;/h2>
&lt;p>UV is a package management tool announced in mid-2024.&lt;br>
It is written in Rust and is characterized by faster performance compared to other package managers.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/032-python-uv/img-032-001.png"
width="585"
height="134"
srcset="https://bossagyu.com/en/blog/032-python-uv/img-032-001_hud269bb2e4d14314d6b8cb2d2b3093c85_13302_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/032-python-uv/img-032-001_hud269bb2e4d14314d6b8cb2d2b3093c85_13302_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Comparison with Other Package Managers"
class="gallery-image"
data-flex-grow="436"
data-flex-basis="1047px"
>&lt;/p>
&lt;p>For official explanations, please refer to &lt;a class="link" href="https://docs.astral.sh/uv/" target="_blank" rel="noopener"
>this site&lt;/a>.&lt;/p>
&lt;h2 id="usage">Usage&lt;/h2>
&lt;h3 id="installing-uv">Installing UV&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">curl -LsSf https://astral.sh/uv/install.sh &lt;span class="p">|&lt;/span> sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Add UV to your PATH:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="nv">$HOME&lt;/span>/.local/bin/env &lt;span class="o">(&lt;/span>sh, bash, zsh&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="nv">$HOME&lt;/span>/.local/bin/env.fish &lt;span class="o">(&lt;/span>fish&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Verify installation:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">uv --version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">uv 0.5.13 &lt;span class="o">(&lt;/span>c456bae5e 2024-12-27&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="how-to-use">How to Use&lt;/h3>
&lt;p>Create a virtual environment:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">uv venv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Activate the virtual environment:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> .venv/bin/activate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install packages:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">uv pip install &amp;lt;package name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For more details on UV, refer to the &lt;a class="link" href="https://docs.astral.sh/uv/" target="_blank" rel="noopener"
>official documentation&lt;/a>.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article explained how to set up a Python development environment on Mac.
As of January 2025, UV is both fast and easy to use, making it a valuable tool for setting up a Python development environment.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/004-python-setup/" >Setting Up a Local Environment Using Pyenv and venv&lt;/a> (traditional Python environment setup)&lt;/li>
&lt;/ul></description></item><item><title>Improving Team Performance by Revisiting Scrum Ceremonies</title><link>https://bossagyu.com/en/blog/028-scrum-ceremony/</link><pubDate>Mon, 18 Nov 2024 09:36:05 +0900</pubDate><guid>https://bossagyu.com/en/blog/028-scrum-ceremony/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>In Scrum teams, ceremonies are essential for aligning team members, sharing progress, addressing challenges, and planning for the next sprint. However, when these ceremonies are conducted out of habit without revisiting their purpose, they can become time-consuming and ineffective.&lt;/p>
&lt;p>This article explores ways to improve team performance by revisiting and optimizing Scrum ceremonies.&lt;/p>
&lt;h2 id="clarify-the-purpose-of-each-ceremony">Clarify the Purpose of Each Ceremony&lt;/h2>
&lt;p>Each Scrum ceremony serves a distinct purpose, and ensuring that the agenda aligns with this purpose is crucial. Below are the purposes of the main Scrum ceremonies, based on the &lt;a class="link" href="https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-US.pdf" target="_blank" rel="noopener"
>Scrum Guide&lt;/a> with some interpretive notes.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Ceremony&lt;/th>
&lt;th>Purpose&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Daily Scrum&lt;/strong>&lt;/td>
&lt;td>Plan the day to achieve the sprint goal.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Sprint Review&lt;/strong>&lt;/td>
&lt;td>Inspect the sprint&amp;rsquo;s outcome and adapt for future work.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Product Backlog Refinement&lt;/strong>&lt;/td>
&lt;td>Organize the backlog to support achieving the product goal.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Sprint Retrospective&lt;/strong>&lt;/td>
&lt;td>Reflect on the sprint to identify improvements for quality and effectiveness.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Sprint Planning&lt;/strong>&lt;/td>
&lt;td>Define the work to be completed during the next sprint.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="enforce-timeboxing">Enforce Timeboxing&lt;/h2>
&lt;p>Adhering to timeboxes is vital for maintaining efficient ceremonies. Timeboxing limits the duration of meetings to a set timeframe, ensuring focus and productivity. If a ceremony exceeds its timebox, reflect on why and plan adjustments to prevent recurrence.&lt;/p>
&lt;h2 id="tips-for-efficient-discussions-in-specific-ceremonies">Tips for Efficient Discussions in Specific Ceremonies&lt;/h2>
&lt;p>Certain ceremonies, like the Daily Scrum, Sprint Review, and Product Backlog Refinement, often consume more time than necessary. Here are some strategies to enhance their effectiveness.&lt;/p>
&lt;h3 id="daily-scrum">Daily Scrum&lt;/h3>
&lt;p>The Daily Scrum should focus on clarifying what the team needs to accomplish that day. Any other discussions should be deferred to smaller, focused meetings involving only relevant members.&lt;/p>
&lt;h4 id="tips">Tips:&lt;/h4>
&lt;ul>
&lt;li>Keep it under &lt;strong>15 minutes&lt;/strong>.&lt;/li>
&lt;li>Limit reporting to essential information.&lt;/li>
&lt;li>Defer in-depth discussions to separate, focused sessions.&lt;/li>
&lt;/ul>
&lt;h3 id="sprint-review">Sprint Review&lt;/h3>
&lt;p>The Sprint Review aims to gather feedback from stakeholders to guide future planning. Prepare for the session by clarifying what feedback is needed and from whom.&lt;/p>
&lt;h4 id="tips-1">Tips:&lt;/h4>
&lt;ul>
&lt;li>&lt;strong>Prepare in advance&lt;/strong> to showcase progress effectively.&lt;/li>
&lt;li>Clearly define what feedback you want and why.&lt;/li>
&lt;/ul>
&lt;h3 id="product-backlog-refinement">Product Backlog Refinement&lt;/h3>
&lt;p>Product Backlog Refinement should ideally take up no more than 10% of the sprint duration. Avoid delving too deeply into items that are not imminent, and focus instead on high-priority tasks.&lt;/p>
&lt;h4 id="tips-2">Tips:&lt;/h4>
&lt;ul>
&lt;li>Avoid excessive detail on future backlog items; focus on what&amp;rsquo;s next.&lt;/li>
&lt;li>Use &lt;strong>relative estimation&lt;/strong> techniques and avoid overly precise estimates, acknowledging inherent uncertainty.&lt;/li>
&lt;/ul>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article discussed methods to conduct Scrum ceremonies more efficiently, ultimately improving team performance. By enforcing timeboxes, aligning discussions with ceremony objectives, and focusing on value-driven conversations, teams can optimize their workflows and deliver better results.&lt;/p></description></item><item><title>How to Read Parquet Files on macOS</title><link>https://bossagyu.com/en/blog/031-read-parquet-file/</link><pubDate>Sun, 15 Sep 2024 16:52:04 +0900</pubDate><guid>https://bossagyu.com/en/blog/031-read-parquet-file/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article introduces a simple way to read Parquet files on macOS via the command line.&lt;/p>
&lt;h2 id="reading-with-parquet-cli">Reading with parquet-cli&lt;/h2>
&lt;p>We will use the &lt;a class="link" href="https://github.com/feast-dev/feast/tree/master/examples/podman_local/feature_repo/data" target="_blank" rel="noopener"
>Parquet&lt;/a> file provided as a sample by Feast.&lt;/p>
&lt;p>Install &lt;code>parquet-cli&lt;/code> on macOS using Homebrew.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">brew install parquet-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Check the meta information.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ parquet meta driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">File path: driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Created by: parquet-cpp-arrow version 18.1.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Properties:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pandas: {&amp;#34;index_columns&amp;#34;: [{&amp;#34;kind&amp;#34;: &amp;#34;range&amp;#34;, &amp;#34;name&amp;#34;: null, &amp;#34;start&amp;#34;: 0, &amp;#34;stop&amp;#34;: 1807, &amp;#34;step&amp;#34;: 1}], &amp;#34;column_indexes&amp;#34;: [{&amp;#34;name&amp;#34;: null, &amp;#34;field_name&amp;#34;: null, &amp;#34;pandas_type&amp;#34;: &amp;#34;unicode&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;object&amp;#34;, &amp;#34;metadata&amp;#34;: {&amp;#34;encoding&amp;#34;: &amp;#34;UTF-8&amp;#34;}}], &amp;#34;columns&amp;#34;: [{&amp;#34;name&amp;#34;: &amp;#34;event_timestamp&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;event_timestamp&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;datetimetz&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;datetime64[ns]&amp;#34;, &amp;#34;metadata&amp;#34;: {&amp;#34;timezone&amp;#34;: &amp;#34;UTC&amp;#34;}}, {&amp;#34;name&amp;#34;: &amp;#34;driver_id&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;driver_id&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;int64&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;int64&amp;#34;, &amp;#34;metadata&amp;#34;: null}, {&amp;#34;name&amp;#34;: &amp;#34;conv_rate&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;conv_rate&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;float32&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;float32&amp;#34;, &amp;#34;metadata&amp;#34;: null}, {&amp;#34;name&amp;#34;: &amp;#34;acc_rate&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;acc_rate&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;float32&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;float32&amp;#34;, &amp;#34;metadata&amp;#34;: null}, {&amp;#34;name&amp;#34;: &amp;#34;avg_daily_trips&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;avg_daily_trips&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;int32&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;int32&amp;#34;, &amp;#34;metadata&amp;#34;: null}, {&amp;#34;name&amp;#34;: &amp;#34;created&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;created&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;datetime&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;datetime64[us]&amp;#34;, &amp;#34;metadata&amp;#34;: null}], &amp;#34;creator&amp;#34;: {&amp;#34;library&amp;#34;: &amp;#34;pyarrow&amp;#34;, &amp;#34;version&amp;#34;: &amp;#34;18.1.0&amp;#34;}, &amp;#34;pandas_version&amp;#34;: &amp;#34;2.2.3&amp;#34;}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ARROW:schema: /////xgGAAAQAAAAAAAKAA4ABgAFAAgACgAAAAABBAAQAAAAAAAKAAwAAAAEAAgACgAAAHAEAAAEAAAAAQAAAAwAAAAIAAwABAAIAAgAAABIBAAABAAAADsEAAB7ImluZGV4X2NvbHVtbnMiOiBbeyJraW5kIjogInJhbmdlIiwgIm5hbWUiOiBudWxsLCAic3RhcnQiOiAwLCAic3RvcCI6IDE4MDcsICJzdGVwIjogMX1dLCAiY29sdW1uX2luZGV4ZXMiOiBbeyJuYW1lIjogbnVsbCwgImZpZWxkX25hbWUiOiBudWxsLCAicGFuZGFzX3R5cGUiOiAidW5pY29kZSIsICJudW1weV90eXBlIjogIm9iamVjdCIsICJtZXRhZGF0YSI6IHsiZW5jb2RpbmciOiAiVVRGLTgifX1dLCAiY29sdW1ucyI6IFt7Im5hbWUiOiAiZXZlbnRfdGltZXN0YW1wIiwgImZpZWxkX25hbWUiOiAiZXZlbnRfdGltZXN0YW1wIiwgInBhbmRhc190eXBlIjogImRhdGV0aW1ldHoiLCAibnVtcHlfdHlwZSI6ICJkYXRldGltZTY0W25zXSIsICJtZXRhZGF0YSI6IHsidGltZXpvbmUiOiAiVVRDIn19LCB7Im5hbWUiOiAiZHJpdmVyX2lkIiwgImZpZWxkX25hbWUiOiAiZHJpdmVyX2lkIiwgInBhbmRhc190eXBlIjogImludDY0IiwgIm51bXB5X3R5cGUiOiAiaW50NjQiLCAibWV0YWRhdGEiOiBudWxsfSwgeyJuYW1lIjogImNvbnZfcmF0ZSIsICJmaWVsZF9uYW1lIjogImNvbnZfcmF0ZSIsICJwYW5kYXNfdHlwZSI6ICJmbG9hdDMyIiwgIm51bXB5X3R5cGUiOiAiZmxvYXQzMiIsICJtZXRhZGF0YSI6IG51bGx9LCB7Im5hbWUiOiAiYWNjX3JhdGUiLCAiZmllbGRfbmFtZSI6ICJhY2NfcmF0ZSIsICJwYW5kYXNfdHlwZSI6ICJmbG9hdDMyIiwgIm51bXB5X3R5cGUiOiAiZmxvYXQzMiIsICJtZXRhZGF0YSI6IG51bGx9LCB7Im5hbWUiOiAiYXZnX2RhaWx5X3RyaXBzIiwgImZpZWxkX25hbWUiOiAiYXZnX2RhaWx5X3RyaXBzIiwgInBhbmRhc190eXBlIjogImludDMyIiwgIm51bXB5X3R5cGUiOiAiaW50MzIiLCAibWV0YWRhdGEiOiBudWxsfSwgeyJuYW1lIjogImNyZWF0ZWQiLCAiZmllbGRfbmFtZSI6ICJjcmVhdGVkIiwgInBhbmRhc190eXBlIjogImRhdGV0aW1lIiwgIm51bXB5X3R5cGUiOiAiZGF0ZXRpbWU2NFt1c10iLCAibWV0YWRhdGEiOiBudWxsfV0sICJjcmVhdG9yIjogeyJsaWJyYXJ5IjogInB5YXJyb3ciLCAidmVyc2lvbiI6ICIxOC4xLjAifSwgInBhbmRhc192ZXJzaW9uIjogIjIuMi4zIn0ABgAAAHBhbmRhcwAABgAAACwBAADcAAAApAAAAHAAAAA0AAAABAAAAPz+//8AAAEKEAAAABgAAAAEAAAAAAAAAAcAAABjcmVhdGVkAGr///8AAAIAKP///wAAAQIQAAAAIAAAAAQAAAAAAAAADwAAAGF2Z19kYWlseV90cmlwcwBo////AAAAASAAAABg////AAABAxAAAAAcAAAABAAAAAAAAAAIAAAAYWNjX3JhdGUAAAAA0v///wAAAQCQ////AAABAxAAAAAgAAAABAAAAAAAAAAJAAAAY29udl9yYXRlAAYACAAGAAYAAAAAAAEAxP///wAAAQIQAAAAJAAAAAQAAAAAAAAACQAAAGRyaXZlcl9pZAAAAAgADAAIAAcACAAAAAAAAAFAAAAAEAAUAAgABgAHAAwAAAAQABAAAAAAAAEKEAAAACgAAAAEAAAAAAAAAA8AAABldmVudF90aW1lc3RhbXAACAAMAAYACAAIAAAAAAADAAQAAAADAAAAVVRDAAAAAAA=
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Schema:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">message schema {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">optional int64 event_timestamp (TIMESTAMP(NANOS,true));
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">optional int64 driver_id;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">optional float conv_rate;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">optional float acc_rate;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">optional int32 avg_daily_trips;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">optional int64 created (TIMESTAMP(MICROS,false));
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Row group 0: count: 1807 16.88 B records start: 4 total(compressed): 29.796 kB total(uncompressed):29.760 kB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--------------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> type encodings count avg size nulls min / max
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">event_timestamp INT64 S _ R 1807 2.78 B 0 &amp;#34;2021-04-12T07:00:00.00000...&amp;#34; / &amp;#34;2024-12-28T14:00:00.00000...&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">driver_id INT64 S _ R 1807 0.07 B 0 &amp;#34;1001&amp;#34; / &amp;#34;1005&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">conv_rate FLOAT S _ R 1807 5.42 B 0 &amp;#34;1.9221554E-4&amp;#34; / &amp;#34;0.9998668&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">acc_rate FLOAT S _ R 1807 5.42 B 0 &amp;#34;2.1329636E-4&amp;#34; / &amp;#34;0.99993944&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">avg_daily_trips INT32 S _ R 1807 3.14 B 0 &amp;#34;0&amp;#34; / &amp;#34;999&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">created INT64 S _ R 1807 0.05 B 0 &amp;#34;2024-12-28T15:20:28.266000&amp;#34; / &amp;#34;2024-12-28T15:20:28.266000&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>View the data using &lt;code>head&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ parquet head driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734102000000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.27734742, &amp;#34;acc_rate&amp;#34;: 0.7152132, &amp;#34;avg_daily_trips&amp;#34;: 823, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734105600000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.57354224, &amp;#34;acc_rate&amp;#34;: 0.9831811, &amp;#34;avg_daily_trips&amp;#34;: 851, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734109200000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.3287562, &amp;#34;acc_rate&amp;#34;: 0.6172164, &amp;#34;avg_daily_trips&amp;#34;: 116, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734112800000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.045716193, &amp;#34;acc_rate&amp;#34;: 0.032996926, &amp;#34;avg_daily_trips&amp;#34;: 741, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734116400000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.12863782, &amp;#34;acc_rate&amp;#34;: 0.8951942, &amp;#34;avg_daily_trips&amp;#34;: 534, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734120000000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.9555806, &amp;#34;acc_rate&amp;#34;: 0.62216556, &amp;#34;avg_daily_trips&amp;#34;: 216, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734123600000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.75297666, &amp;#34;acc_rate&amp;#34;: 0.37602386, &amp;#34;avg_daily_trips&amp;#34;: 954, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734127200000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.46957988, &amp;#34;acc_rate&amp;#34;: 0.6454945, &amp;#34;avg_daily_trips&amp;#34;: 360, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734130800000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.6702387, &amp;#34;acc_rate&amp;#34;: 0.36532214, &amp;#34;avg_daily_trips&amp;#34;: 396, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734134400000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.019627139, &amp;#34;acc_rate&amp;#34;: 0.528229, &amp;#34;avg_daily_trips&amp;#34;: 833, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Check the schema.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ parquet schema driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;type&amp;#34; : &amp;#34;record&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;name&amp;#34; : &amp;#34;schema&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;fields&amp;#34; : [ {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;name&amp;#34; : &amp;#34;event_timestamp&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;long&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;name&amp;#34; : &amp;#34;driver_id&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;long&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;name&amp;#34; : &amp;#34;conv_rate&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;float&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;name&amp;#34; : &amp;#34;acc_rate&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;float&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;name&amp;#34; : &amp;#34;avg_daily_trips&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;int&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;name&amp;#34; : &amp;#34;created&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;type&amp;#34; : &amp;#34;long&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;logicalType&amp;#34; : &amp;#34;local-timestamp-micros&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">} ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">} ]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>This article explained how to easily read Parquet files on macOS.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/033-feast-tutorial/" >Running the Feast Tutorial on macOS&lt;/a> (using Feast with Parquet files)&lt;/li>
&lt;/ul></description></item><item><title>Setting Up a Go Development Environment on Mac and Running Hello World</title><link>https://bossagyu.com/en/blog/030-go-environment-construction/</link><pubDate>Sun, 15 Sep 2024 16:52:04 +0900</pubDate><guid>https://bossagyu.com/en/blog/030-go-environment-construction/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This guide introduces how to quickly set up a Go development environment on Mac and run a Hello World program.&lt;/p>
&lt;h2 id="installing-go">Installing Go&lt;/h2>
&lt;p>Install Go using Homebrew.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; brew install go
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Check the installed version.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; go version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">go version go1.21.3 darwin/arm64
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="running-hello-world">Running Hello World&lt;/h2>
&lt;p>Save the following code as &lt;code>main.go&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">package&lt;/span> &lt;span class="nx">main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="s">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Hello World\n&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Run the code.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; go run hello.go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Hello World
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Build the binary and execute it.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; go build hello.go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; ls
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hello* hello.go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; ./hello
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Hello World
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>That&amp;rsquo;s how you can set up a Go development environment on Mac and quickly run a Hello World program.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/029-grpc/" >Research on gRPC&lt;/a> (practical gRPC implementation)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/041-redis-go/" >How to Use Redis with Go&lt;/a> (Redis + Go in practice)&lt;/li>
&lt;/ul></description></item><item><title>Research on gRPC</title><link>https://bossagyu.com/en/blog/029-grpc/</link><pubDate>Sun, 01 Sep 2024 17:53:57 +0900</pubDate><guid>https://bossagyu.com/en/blog/029-grpc/</guid><description>&lt;h2 id="what-is-grpc">What is gRPC?&lt;/h2>
&lt;ul>
&lt;li>gRPC is a protocol developed by Google to implement RPC.&lt;/li>
&lt;li>It uses Protocol Buffers to serialize data, enabling high-speed communication.&lt;/li>
&lt;li>API specifications are pre-defined in a &lt;code>.proto&lt;/code> file using IDL, from which source code for both the server and client sides is generated.&lt;/li>
&lt;/ul>
&lt;p>Differences between REST and gRPC&lt;/p>
&lt;ul>
&lt;li>REST is resource-oriented, whereas RPC focuses on method invocation, with data being a byproduct.&lt;/li>
&lt;/ul>
&lt;h2 id="advantages-and-disadvantages">Advantages and Disadvantages&lt;/h2>
&lt;h3 id="advantages">Advantages&lt;/h3>
&lt;ul>
&lt;li>High performance with HTTP/2&lt;/li>
&lt;li>Data transfer via Protocol Buffers
&lt;ul>
&lt;li>Development follows a schema-first approach due to the use of IDL&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Flexible streaming modes&lt;/li>
&lt;/ul>
&lt;h3 id="disadvantages">Disadvantages&lt;/h3>
&lt;ul>
&lt;li>Non-compliance with HTTP/2&lt;/li>
&lt;li>Limited browser support&lt;/li>
&lt;li>Variability in feature implementation depending on the programming language&lt;/li>
&lt;li>Serialized binary data is not human-readable&lt;/li>
&lt;li>REST is also sufficiently fast&lt;/li>
&lt;/ul>
&lt;h2 id="proto-files">.proto Files&lt;/h2>
&lt;p>gRPC uses Protocol Buffers as its serialization format.&lt;br>
Schema definitions are made in files with the &lt;code>.proto&lt;/code> extension, and code for various languages is generated using the &lt;code>protoc&lt;/code> command.&lt;br>
In Protocol Buffers, all values have types, which can be divided into scalar types and message types.&lt;/p>
&lt;h3 id="scalar-types">Scalar Types&lt;/h3>
&lt;ul>
&lt;li>Numeric, string, boolean, byte array&lt;/li>
&lt;/ul>
&lt;h3 id="message-types">Message Types&lt;/h3>
&lt;ul>
&lt;li>Message types with multiple fields&lt;/li>
&lt;li>Multiple message types can be defined in a single &lt;code>.proto&lt;/code> file&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">message Person &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> int32 &lt;span class="nv">id&lt;/span> &lt;span class="o">=&lt;/span> 1&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> string &lt;span class="nv">name&lt;/span> &lt;span class="o">=&lt;/span> 2&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> string &lt;span class="nv">email&lt;/span> &lt;span class="o">=&lt;/span> 3&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="conducting-the-grpc-quick-start">Conducting the gRPC Quick Start&lt;/h2>
&lt;p>This time, we will perform the gRPC Quick Start using a Python environment.&lt;br>
&lt;a class="link" href="https://grpc.io/docs/languages/python/quickstart/" target="_blank" rel="noopener"
>https://grpc.io/docs/languages/python/quickstart/&lt;/a>&lt;/p>
&lt;p>Set up the necessary Python environment.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python -m pip install grpcio
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python -m pip install grpcio-tools
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Download the sample code.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git clone -b v1.64.0 --depth &lt;span class="m">1&lt;/span> --shallow-submodules https://github.com/grpc/grpc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> grpc/examples/python/helloworld
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Start the server.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python greeter_server.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Server started, listening on &lt;span class="m">50051&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Open another terminal and start the client.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python greeter_client.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">## Response&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Will try to greet world ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Greeter client received: Hello, you!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Communication between the gRPC client and server was successfully established.&lt;/p>
&lt;h2 id="modifying-the-proto-file">Modifying the .proto File&lt;/h2>
&lt;p>This time, we will modify the &lt;code>helloworld.proto&lt;/code> file to add a new method.&lt;/p>
&lt;p>Move to the directory containing the &lt;code>helloworld.proto&lt;/code> file.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> grpc/examples/protos
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Modify the file as follows:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">syntax&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;proto3&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">option&lt;/span> &lt;span class="n">java_multiple_files&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">option&lt;/span> &lt;span class="n">java_package&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;io.grpc.examples.helloworld&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">option&lt;/span> &lt;span class="n">java_outer_classname&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HelloWorldProto&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">option&lt;/span> &lt;span class="n">objc_class_prefix&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HLW&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">package&lt;/span> &lt;span class="n">helloworld&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">//&lt;/span> &lt;span class="n">The&lt;/span> &lt;span class="n">greeting&lt;/span> &lt;span class="n">service&lt;/span> &lt;span class="n">definition&lt;/span>&lt;span class="o">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">service&lt;/span> &lt;span class="n">Greeter&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Sends&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="n">greeting&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rpc&lt;/span> &lt;span class="n">SayHello&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">returns&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloReply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Add&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">following&lt;/span> &lt;span class="n">line&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rpc&lt;/span> &lt;span class="n">SayHelloAgain&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">returns&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloReply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rpc&lt;/span> &lt;span class="n">SayHelloStreamReply&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">returns&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">stream&lt;/span> &lt;span class="n">HelloReply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rpc&lt;/span> &lt;span class="n">SayHelloBidiStream&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">stream&lt;/span> &lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">returns&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">stream&lt;/span> &lt;span class="n">HelloReply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">//&lt;/span> &lt;span class="n">The&lt;/span> &lt;span class="n">request&lt;/span> &lt;span class="n">message&lt;/span> &lt;span class="n">containing&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="s1">&amp;#39;s name.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="n">HelloRequest&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">string&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">//&lt;/span> &lt;span class="n">The&lt;/span> &lt;span class="n">response&lt;/span> &lt;span class="n">message&lt;/span> &lt;span class="n">containing&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">greetings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="n">HelloReply&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">string&lt;/span> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Generate the gRPC code.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> examples/python/helloworld
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python -m grpc_tools.protoc -I../../protos --python_out&lt;span class="o">=&lt;/span>. --pyi_out&lt;span class="o">=&lt;/span>. --grpc_python_out&lt;span class="o">=&lt;/span>. ../../protos/helloworld.proto
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The following files are regenerated.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ls -l
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r--@ &lt;span class="m">1&lt;/span> xx xx &lt;span class="m">1823&lt;/span> &lt;span class="m">9&lt;/span> &lt;span class="m">1&lt;/span> 18:12 helloworld_pb2.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r--@ &lt;span class="m">1&lt;/span> xx xx &lt;span class="m">578&lt;/span> &lt;span class="m">9&lt;/span> &lt;span class="m">1&lt;/span> 18:12 helloworld_pb2.pyi
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r--@ &lt;span class="m">1&lt;/span> xx xx &lt;span class="m">7018&lt;/span> &lt;span class="m">9&lt;/span> &lt;span class="m">1&lt;/span> 18:12 helloworld_pb2_grpc.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The &lt;code>_pd&lt;/code> file that is updated contains the auto-generated Protocol Buffers definition classes and is generally not manually modified.&lt;/p>
&lt;p>Update the &lt;code>greeter_server.py&lt;/code> file.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">concurrent&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">futures&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">logging&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">grpc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">helloworld_pb2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">helloworld_pb2_grpc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Greeter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">helloworld_pb2_grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GreeterServicer&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">SayHello&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">helloworld_pb2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">HelloReply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Hello, &lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s2">!&amp;#34;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Add the following function&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">SayHelloAgain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">helloworld_pb2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">HelloReply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Hello Again, &lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s2">!&amp;#34;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">serve&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">port&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;50051&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">server&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">futures&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ThreadPoolExecutor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_workers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">helloworld_pb2_grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_GreeterServicer_to_server&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Greeter&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">server&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_insecure_port&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;[::]:&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">port&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Server started, listening on &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">port&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">wait_for_termination&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logging&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">basicConfig&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">serve&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Update the &lt;code>greeter_client.py&lt;/code> file.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># NOTE(gRPC Python Team): .close() is possible on a channel and should be&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># used in circumstances in which the with statement does not fit the needs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># of the code.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Will try to greet world ...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="n">grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">insecure_channel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;localhost:50051&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">channel&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stub&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">helloworld_pb2_grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GreeterStub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">channel&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">stub&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SayHello&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">helloworld_pb2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;you&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Greeter client received: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">stub&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SayHelloAgain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">helloworld_pb2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;you&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Greeter client received: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Restart the server and run the client.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python greeter_server.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python greeter_client.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Greeter client received: Hello, you!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Greeter client received: Hello Again, you!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The newly added method is confirmed to be functioning correctly.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This time, we researched gRPC and followed the official documentation tutorial.
gRPC allows for schema-first development and enables high-performance communication via HTTP/2. Recently, it has been gaining attention as an alternative to REST APIs, making it a technology worth familiarizing yourself with.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/030-go-environment-construction/" >Setting Up a Go Development Environment on Mac and Running Hello World&lt;/a> (Go environment setup)&lt;/li>
&lt;/ul></description></item><item><title>Introduction to ChatGPT 4o</title><link>https://bossagyu.com/en/blog/027-chatgpt-4o/</link><pubDate>Tue, 14 May 2024 23:22:39 +0900</pubDate><guid>https://bossagyu.com/en/blog/027-chatgpt-4o/</guid><description>&lt;h2 id="introduction-of-gpt-4o">Introduction of GPT-4o&lt;/h2>
&lt;p>On May 13, 2024, OpenAI announced a new GPT model, ChatGPT-4o.&lt;/p>
&lt;p>GPT-4o has the following improvements compared to previous models:&lt;/p>
&lt;ul>
&lt;li>Realization of natural conversation&lt;/li>
&lt;li>Faster response time&lt;/li>
&lt;li>Enhanced multilingual support&lt;/li>
&lt;li>Higher reliability&lt;/li>
&lt;/ul>
&lt;p>Notably, the improvements in inference speed and quality allow it to perform exceptionally well in real-time dialogue systems, enabling smoother and more natural communication.&lt;/p>
&lt;h2 id="real-time-dialogue-applications">Real-Time Dialogue Applications&lt;/h2>
&lt;p>The following video demonstrates real-time interaction with ChatGPT-4o using a smartphone.&lt;/p>
&lt;div class="video-wrapper">
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/vgYi3Wr7v_g"
allowfullscreen
title="YouTube Video"
>
&lt;/iframe>
&lt;/div>
&lt;p>As you can see in the video, ChatGPT-4o can respond at a level close to human conversation.&lt;br>
This allows users to enjoy very natural conversations.&lt;/p>
&lt;p>Previously, the ChatGPT series converted voice to text and then inputted it into GPT-4 for dialogue.&lt;br>
This meant that emotional information such as tone was lost in the process.&lt;/p>
&lt;p>However, ChatGPT-4o is trained directly from voice, taking into account information such as tone, leading to more natural conversations.&lt;br>
Additionally, it returns responses directly from voice, eliminating the text interpretation step and allowing for faster responses.&lt;/p>
&lt;h2 id="enhanced-multilingual-support">Enhanced Multilingual Support&lt;/h2>
&lt;p>GPT-4o also enhances multilingual support.&lt;br>
Previously, questions asked in English received high-accuracy responses, but questions in Japanese resulted in lower accuracy.&lt;/p>
&lt;p>With the enhanced multilingual support, high-accuracy responses can now be obtained even in Japanese.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This time, we introduced ChatGPT-4o, announced by OpenAI.&lt;br>
The accuracy of the ChatGPT model has remarkably improved, feeling more and more like human conversation.&lt;br>
I believe that how well engineers can utilize generative AI will greatly affect productivity, so I will continue to monitor trends closely.&lt;/p>
&lt;h2 id="related-information">Related Information&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://openai.com/index/hello-gpt-4o/" target="_blank" rel="noopener"
>Hello ChatGPT-4o&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/023-chatgpt-create-image/" >Generating Images with ChatGPT&lt;/a> (image generation with ChatGPT)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/044-openai-response-api/" >How to Use the OpenAI Response API&lt;/a> (OpenAI API integration)&lt;/li>
&lt;/ul></description></item><item><title>Migrating from Evernote to Notion</title><link>https://bossagyu.com/en/blog/026-evernote-to-notion/</link><pubDate>Mon, 29 Apr 2024 19:32:38 +0900</pubDate><guid>https://bossagyu.com/en/blog/026-evernote-to-notion/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>As of April 26, 2024, Evernote&amp;rsquo;s Japanese corporation was dissolved, which does not mean Evernote services are ending, but it certainly raises concerns about its future. Given the many restrictions on Evernote&amp;rsquo;s free plan and the cost associated with its premium plan, I considered switching to another note-taking app.&lt;/p>
&lt;p>After some deliberation, I chose Notion for the following reasons:&lt;/p>
&lt;ol>
&lt;li>It possesses all essential features required for a note-taking app and is a viable alternative to Evernote.&lt;/li>
&lt;li>Notion&amp;rsquo;s free plan is less restrictive and adequate for my needs, allowing me to cut costs on premium subscriptions.&lt;/li>
&lt;li>Notion offers an import tool specifically for Evernote, facilitating a low-cost migration.&lt;/li>
&lt;/ol>
&lt;p>This article details the process of migrating from Evernote to Notion.&lt;/p>
&lt;h2 id="migration-process">Migration Process&lt;/h2>
&lt;p>Notion provides an Evernote import feature, making the migration straightforward:&lt;/p>
&lt;h3 id="accessing-import-settings">Accessing Import Settings&lt;/h3>
&lt;p>Navigate to &lt;code>Settings&lt;/code> in the Notion app.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/026-evernote-to-notion/img-026-001.png"
width="476"
height="109"
srcset="https://bossagyu.com/en/blog/026-evernote-to-notion/img-026-001_hue34c4fc34d425abb2cb8a7822cb5448a_12099_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/026-evernote-to-notion/img-026-001_hue34c4fc34d425abb2cb8a7822cb5448a_12099_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Notion Settings"
class="gallery-image"
data-flex-grow="436"
data-flex-basis="1048px"
>&lt;/p>
&lt;p>Click &lt;code>Settings&lt;/code> to access the &lt;code>Import&lt;/code> option.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/026-evernote-to-notion/img-026-002.png"
width="1107"
height="553"
srcset="https://bossagyu.com/en/blog/026-evernote-to-notion/img-026-002_hu5671edaff82d6b0627f9e68841ee4894_113782_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/026-evernote-to-notion/img-026-002_hu5671edaff82d6b0627f9e68841ee4894_113782_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Notion Import"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="480px"
>&lt;/p>
&lt;p>After clicking on &lt;code>Import&lt;/code>, select &lt;code>Evernote&lt;/code> as the import source.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/026-evernote-to-notion/img-026-003.png"
width="262"
height="131"
srcset="https://bossagyu.com/en/blog/026-evernote-to-notion/img-026-003_hu22ab7887a397f6d4d5c09ac7940595a1_12909_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/026-evernote-to-notion/img-026-003_hu22ab7887a397f6d4d5c09ac7940595a1_12909_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Select Evernote"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="480px"
>&lt;/p>
&lt;h3 id="importing-notebooks">Importing Notebooks&lt;/h3>
&lt;p>Once linked, you can choose the notebooks you wish to import. Here&amp;rsquo;s a critical tip:
While it seems possible to select multiple notebooks for import, doing so may lead to excessively long processing times or even errors. Therefore, it is advisable to import one notebook at a time.&lt;/p>
&lt;p>Even when importing one notebook at a time, be prepared for the process to take several hours, depending on the size of the notebook.&lt;/p>
&lt;h3 id="post-import-review">Post-Import Review&lt;/h3>
&lt;p>After the import, the formatting, images, links, and labels were preserved well, allowing for a seamless transition to Notion.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>This guide outlines the steps to migrate from Evernote to Notion. With Notion&amp;rsquo;s robust free features and straightforward migration process, it&amp;rsquo;s worth considering if you&amp;rsquo;re looking to move away from Evernote.&lt;/p></description></item><item><title>Setting Up a Global gitignore for All Projects</title><link>https://bossagyu.com/en/blog/025-git-ignore/</link><pubDate>Tue, 16 Apr 2024 23:16:25 +0900</pubDate><guid>https://bossagyu.com/en/blog/025-git-ignore/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Adding a &lt;code>.gitignore&lt;/code> file to a project allows you to exclude certain files and directories from being tracked by git. However, it can be tedious to repeatedly add default directories like &lt;code>.idea&lt;/code> generated by IDEs to &lt;code>.gitignore&lt;/code> for each new project. This article explains how to apply &lt;code>.gitignore&lt;/code> settings globally across all projects.&lt;/p>
&lt;h2 id="applying-gitignore-globally">Applying gitignore Globally&lt;/h2>
&lt;p>Git by default checks for ignore settings in &lt;code>~/.config/git/ignore&lt;/code>. Therefore, by placing your ignore configurations in this file, you can have them applied to all your projects.&lt;/p>
&lt;p>While it&amp;rsquo;s common to create a &lt;code>.gitignore_global&lt;/code> and register it under &lt;code>core.excludesfile&lt;/code>, this method requires unnecessary configuration in &lt;code>.gitconfig&lt;/code>, which is why the approach described here is recommended.&lt;/p>
&lt;h2 id="steps-to-apply-gitignore-globally">Steps to Apply gitignore Globally&lt;/h2>
&lt;h3 id="create-a-directory-for-ignore-file">Create a Directory for Ignore File&lt;/h3>
&lt;p>Create a directory to store your ignore file, if it doesn&amp;rsquo;t already exist:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mkdir -p ~/.config/git/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="create-and-configure-the-ignore-file">Create and Configure the Ignore File&lt;/h3>
&lt;p>Create the ignore file and write the patterns for files and directories you want to ignore globally:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">vim ~/.config/git/ignore
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Example of what to include:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">.idea/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.log
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">node_modules/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="applying-the-ignore-settings">Applying the Ignore Settings&lt;/h3>
&lt;p>These settings will now automatically apply to all projects managed under your user account. If there are already tracked files you wish to ignore, use &lt;code>git rm --cached&lt;/code> to stop tracking them without deleting the files.&lt;/p>
&lt;h2 id="reference">Reference&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://git-scm.com/docs/gitignore" target="_blank" rel="noopener"
>Git - gitignore&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>By following these steps, you can efficiently manage your ignore settings across all your git projects without having to replicate configurations in each project repository.&lt;/p></description></item><item><title>Using Bluesky API for Automated Posting with Python</title><link>https://bossagyu.com/en/blog/024-bluesky-api/</link><pubDate>Sun, 07 Apr 2024 23:52:09 +0900</pubDate><guid>https://bossagyu.com/en/blog/024-bluesky-api/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>&lt;a class="link" href="https://bsky.app/" target="_blank" rel="noopener"
>Bluesky&lt;/a> is a decentralized social network initiated by Jack Dorsey, former CEO of Twitter.&lt;br>
Built on the &lt;a class="link" href="https://atproto.com/docs" target="_blank" rel="noopener"
>ATProtocol&lt;/a>, it can be seen as a decentralized version of Twitter, where there is no central authority. The move towards decentralization in social networking might be following the trend seen in finance, from centralized to decentralized cryptocurrencies.&lt;/p>
&lt;p>This article compiles methods for executing Bluesky&amp;rsquo;s API using Python.&lt;/p>
&lt;h2 id="steps-to-use-bluesky-api">Steps to Use Bluesky API&lt;/h2>
&lt;ul>
&lt;li>Generating an API Password&lt;/li>
&lt;li>Setting Up Python Environment&lt;/li>
&lt;li>Script Creation and Execution&lt;/li>
&lt;/ul>
&lt;h2 id="generating-an-api-password">Generating an API Password&lt;/h2>
&lt;p>To execute the API, you need to generate an API password using your account name.&lt;/p>
&lt;p>First, verify your account name displayed on your Bluesky profile, minus the initial &lt;code>@&lt;/code>. For instance, if my account is &lt;code>bossagyu.bsky.social&lt;/code>, that&amp;rsquo;s the account name I&amp;rsquo;ll use.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/024-bluesky-api/img-024-001.png"
width="1246"
height="494"
srcset="https://bossagyu.com/en/blog/024-bluesky-api/img-024-001_huf800b51d6508b8d55f734f90e398ecce_69330_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/024-bluesky-api/img-024-001_huf800b51d6508b8d55f734f90e398ecce_69330_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Bluesky Account Name"
class="gallery-image"
data-flex-grow="252"
data-flex-basis="605px"
>&lt;/p>
&lt;p>Next, generate the API execution password.&lt;/p>
&lt;p>Navigate to &lt;code>Settings&lt;/code> → &lt;code>App Passwords&lt;/code> to create the password.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/024-bluesky-api/img-024-002.png"
width="702"
height="599"
srcset="https://bossagyu.com/en/blog/024-bluesky-api/img-024-002_hu4ae5c012eccb5c32725cc4f577581c4d_72656_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/024-bluesky-api/img-024-002_hu4ae5c012eccb5c32725cc4f577581c4d_72656_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Bluesky App Password 1"
class="gallery-image"
data-flex-grow="117"
data-flex-basis="281px"
>&lt;/p>
&lt;p>Click &lt;code>Add App Password&lt;/code>.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/024-bluesky-api/img-024-003.png"
width="589"
height="281"
srcset="https://bossagyu.com/en/blog/024-bluesky-api/img-024-003_hu581c5fcbb0f6fca5bc47923c36751a59_43772_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/024-bluesky-api/img-024-003_hu581c5fcbb0f6fca5bc47923c36751a59_43772_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Bluesky App Password 2"
class="gallery-image"
data-flex-grow="209"
data-flex-basis="503px"
>&lt;/p>
&lt;p>After clicking the add button, name your password for management purposes. The name itself is not the password but helps with organization.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/024-bluesky-api/img-024-004.png"
width="588"
height="245"
srcset="https://bossagyu.com/en/blog/024-bluesky-api/img-024-004_hude180766a2493746a1dec1e2149d4e83_41902_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/024-bluesky-api/img-024-004_hude180766a2493746a1dec1e2149d4e83_41902_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Bluesky App Password 3"
class="gallery-image"
data-flex-grow="240"
data-flex-basis="576px"
>&lt;/p>
&lt;p>Once the password is generated, ensure to copy it as it won&amp;rsquo;t be displayed again. If you forget to copy, simply generate a new one.&lt;/p>
&lt;h2 id="setting-up-the-python-environment">Setting Up the Python Environment&lt;/h2>
&lt;p>Prepare your Python environment. For setup using venv, refer to &lt;a class="link" href="https://bossagyu.com/blog/004-python-setup/" target="_blank" rel="noopener"
>this guide&lt;/a>.&lt;/p>
&lt;p>According to the &lt;a class="link" href="https://atproto.com/docs" target="_blank" rel="noopener"
>official documentation&lt;/a>, Python version 3.7.1 or higher is required.&lt;/p>
&lt;p>After setting up your environment, install the ATProtocol library to interact with it.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ pip install atproto
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Verify installation:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ pip list &lt;span class="p">|&lt;/span> grep atproto
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">atproto 0.0.46
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>With this, preparation is complete.&lt;/p>
&lt;h2 id="script-creation-and-execution">Script Creation and Execution&lt;/h2>
&lt;p>Create a script to post on Bluesky.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">atproto&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Client&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Client&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">user_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;bossagyu.bsky.social&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">password&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;*******&amp;#34;&lt;/span> &lt;span class="c1"># Enter the generated API password&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">login&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">password&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">send_post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;Posting via API.&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>That&amp;rsquo;s all for the script. You can now post to Bluesky using the API.&lt;/p>
&lt;p>Let&amp;rsquo;s run it:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ python post_bluesky.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Upon execution, the post successfully appears on Bluesky.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/024-bluesky-api/img-024-005.png"
width="1244"
height="512"
srcset="https://bossagyu.com/en/blog/024-bluesky-api/img-024-005_hu4f39cd8b24c00a6a84572b28046e189d_95124_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/024-bluesky-api/img-024-005_hu4f39cd8b24c00a6a84572b28046e189d_95124_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Posted on Bluesky"
class="gallery-image"
data-flex-grow="242"
data-flex-basis="583px"
>&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>This article covered how to execute Bluesky&amp;rsquo;s API using Python.
While Bluesky is still under development, unlike Twitter, which has limitations and charges for API use, Bluesky offers a free API, making it an excellent option for those looking to experiment with social networking APIs at no cost.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/004-python-setup/" >Setting Up a Local Environment Using Pyenv and venv&lt;/a> (Python environment setup)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/032-python-uv/" >Setting Up a Python Development Environment on Mac with UV&lt;/a> (modern Python environment setup)&lt;/li>
&lt;/ul></description></item><item><title>Generating Images with ChatGPT</title><link>https://bossagyu.com/en/blog/023-chatgpt-create-image/</link><pubDate>Sun, 31 Mar 2024 17:35:07 +0900</pubDate><guid>https://bossagyu.com/en/blog/023-chatgpt-create-image/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Unlike image generation models like Stable Diffusion, ChatGPT can also generate images. This guide explains how to do so.&lt;br>
For those using the ChatGPT paid plan, images can be generated without any additional fees, allowing for the creation of commercially usable images with minimal effort.&lt;/p>
&lt;p>This tutorial utilizes DALL-E, a feature of ChatGPT Plus, to generate images.
For more information on DALL-E3, refer to OpenAI&amp;rsquo;s &lt;a class="link" href="https://openai.com/dall-e-3" target="_blank" rel="noopener"
>official page&lt;/a>.&lt;/p>
&lt;h2 id="how-to-generate-images">How to Generate Images&lt;/h2>
&lt;p>Select &amp;ldquo;Explore GPTs&amp;rdquo; from the sidebar.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-001.png"
width="265"
height="116"
srcset="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-001_hud4b55173ac45fc1d51204f1be6812280_8545_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-001_hud4b55173ac45fc1d51204f1be6812280_8545_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Explore GPTs"
class="gallery-image"
data-flex-grow="228"
data-flex-basis="548px"
>&lt;/p>
&lt;p>Type &lt;code>DALL-E&lt;/code> in the search bar and perform a search.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-002.png"
width="1303"
height="365"
srcset="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-002_hu7cb4a7e18b89e3c24f69774acd94f41c_41824_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-002_hu7cb4a7e18b89e3c24f69774acd94f41c_41824_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="DALL-E"
class="gallery-image"
data-flex-grow="356"
data-flex-basis="856px"
>&lt;/p>
&lt;p>Click on &amp;ldquo;Start Chat&amp;rdquo; to begin generating images.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-003.png"
width="1145"
height="540"
srcset="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-003_huafd50c1ff19a49756eba7cc08e0d5a58_52595_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-003_huafd50c1ff19a49756eba7cc08e0d5a58_52595_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Start Chat"
class="gallery-image"
data-flex-grow="212"
data-flex-basis="508px"
>&lt;/p>
&lt;p>Then, simply enter a description of the image you wish to generate, and the image will be created.&lt;/p>
&lt;h2 id="generating-an-example-image">Generating an Example Image&lt;/h2>
&lt;p>Let&amp;rsquo;s try generating an image of a dog wearing pink sunglasses, as used in this blog.&lt;/p>
&lt;p>For a start, we&amp;rsquo;ll input the prompt &amp;ldquo;a dog wearing pink sunglasses&amp;rdquo;.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-004.png"
width="986"
height="599"
srcset="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-004_hu5e0a02deff970890b46e4e180aa8365f_498318_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-004_hu5e0a02deff970890b46e4e180aa8365f_498318_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="ChatGPT generated image 1"
class="gallery-image"
data-flex-grow="164"
data-flex-basis="395px"
>&lt;/p>
&lt;p>Additionally, like using regular ChatGPT, you can refine the generated image by entering additional prompts.&lt;br>
This time, let&amp;rsquo;s input &amp;ldquo;Make it anime-style&amp;rdquo; as an additional prompt.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-005.png"
width="1055"
height="549"
srcset="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-005_hu987fb5d0bfb771c9b7617c23bc8f5dba_474840_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-005_hu987fb5d0bfb771c9b7617c23bc8f5dba_474840_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="ChatGPT generated image 2"
class="gallery-image"
data-flex-grow="192"
data-flex-basis="461px"
>&lt;/p>
&lt;p>You can see it has an anime-style appearance.
By issuing further orders, you can gradually get closer to your desired image.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>This guide has explained how to generate images using ChatGPT.&lt;/p>
&lt;p>It&amp;rsquo;s incredibly convenient to be able to generate images easily, but just like with Stable Diffusion, it&amp;rsquo;s quite challenging to get the exact image you want without adjusting the prompts.
The need for prompt refinement seems less critical than with Stable Diffusion, suggesting a possible difference in model performance.&lt;/p>
&lt;p>Note that if you overuse this feature, you may receive a message asking you to wait, indicating there may be a limit on the number of generations.
For those with access to a machine source, running Stable Diffusion locally on a Mac might still be the best option.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-006.png"
width="866"
height="238"
srcset="https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-006_hue0d0043ad8a0a3a907671d165ec53110_40149_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/023-chatgpt-create-image/img-023-006_hue0d0043ad8a0a3a907671d165ec53110_40149_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="ChatGPT image generation limit"
class="gallery-image"
data-flex-grow="363"
data-flex-basis="873px"
>&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/027-chatgpt-4o/" >Introduction to ChatGPT 4o&lt;/a> (overview of ChatGPT 4o features)&lt;/li>
&lt;/ul></description></item><item><title>Using Enums in TypeScript</title><link>https://bossagyu.com/en/blog/022-typescript-enum/</link><pubDate>Sat, 23 Mar 2024 13:11:13 +0900</pubDate><guid>https://bossagyu.com/en/blog/022-typescript-enum/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to use Enums in TypeScript.&lt;/p>
&lt;h2 id="what-are-enums">What are Enums?&lt;/h2>
&lt;p>Enums (enumerated types) represent a collection of related values. While many languages implement Enums, JavaScript does not. However, TypeScript supports Enums, enriching the JavaScript experience.&lt;/p>
&lt;h2 id="how-to-use-enums">How to Use Enums&lt;/h2>
&lt;p>Enums can be defined as follows:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">enum&lt;/span> &lt;span class="nx">Status&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">zero&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">one&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">two&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">zero&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 0
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>By default, Enums are assigned numerical values, starting from 0. The compiled JavaScript code would look like this:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">Status&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;zero&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;zero&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;one&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;one&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;two&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;two&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})(&lt;/span>&lt;span class="nx">Status&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">zero&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 0
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You can also assign string values to Enums:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">enum&lt;/span> &lt;span class="nx">Status&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">zero&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;zero&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">one&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;one&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">two&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;two&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">zero&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// &amp;#39;zero&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>When comparing string values, you can write as follows:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">stringZero&lt;/span>: &lt;span class="kt">String&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;zero&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">stringZero&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="nx">StringStatus&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">StringStatus&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">zero&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;value is zero&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;value is not zero&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article explained how to use Enums in TypeScript. By utilizing Enums, you can improve the readability and maintainability of your code.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/021-typescript-setup/" >Setting Up TypeScript Development Environment Easily with Volta&lt;/a> (TypeScript environment setup)&lt;/li>
&lt;/ul></description></item><item><title>Setting Up TypeScript Development Environment Easily with Volta</title><link>https://bossagyu.com/en/blog/021-typescript-setup/</link><pubDate>Sun, 10 Mar 2024 13:11:13 +0900</pubDate><guid>https://bossagyu.com/en/blog/021-typescript-setup/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to easily set up a TypeScript development environment using Volta, with MacOS as the target platform.&lt;/p>
&lt;h2 id="what-is-volta">What is Volta?&lt;/h2>
&lt;p>Volta is a version management tool for Node.js, featuring the following characteristics as presented on &lt;a class="link" href="https://volta.sh/" target="_blank" rel="noopener"
>Volta&amp;rsquo;s official site&lt;/a>:&lt;/p>
&lt;ul>
&lt;li>Fast: Built in Rust, enabling swift Node.js version switching.&lt;/li>
&lt;li>Reliable: Ensures everyone on a project uses the same tools.&lt;/li>
&lt;li>Universal: Can be used across package managers, node runtimes, and OSes.&lt;/li>
&lt;/ul>
&lt;p>While nodebrew has been commonly used, the use of Volta seems to be increasing lately.&lt;/p>
&lt;h2 id="installing-volta-and-nodejs">Installing Volta and Node.js&lt;/h2>
&lt;p>Installing Volta is as simple as running the following command:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">curl https://get.volta.sh &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If you&amp;rsquo;re using zsh and the path hasn&amp;rsquo;t been automatically set, use the following commands:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;VOLTA_HOME=$HOME/.volta&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PATH=$VOLTA_HOME/bin:$PATH&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> ~/.zshrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Verify the installation. If a version is displayed, Volta has been successfully installed:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta -v
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Next, use Volta to install Node.js. If no version is specified, the latest LTS version will be installed:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta install node
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="installing-yarn-and-creating-a-typescript-project">Installing yarn and Creating a TypeScript Project&lt;/h2>
&lt;h3 id="differences-between-npm-and-yarn">Differences between npm and yarn&lt;/h3>
&lt;p>Both npm and yarn are package managers for Node.js. Their features include:&lt;/p>
&lt;p>&lt;strong>npm&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Released a year after Node.js (2010).&lt;/li>
&lt;li>Stands for Node Package Manager.&lt;/li>
&lt;li>Automatically generates a package-lock.json file.&lt;/li>
&lt;li>Installed automatically with Node.js.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>yarn&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Released in 2016.&lt;/li>
&lt;li>Developed by Facebook, Google, Exponent, and Tilde.&lt;/li>
&lt;li>Compatible with npm (can use the same package.json).&lt;/li>
&lt;li>More strict in locking module versions than npm.&lt;/li>
&lt;li>Faster installation than npm.&lt;/li>
&lt;/ul>
&lt;p>While yarn appears superior, recent updates to npm have minimized the differences. Here, we will use yarn to create a TypeScript project.&lt;/p>
&lt;h3 id="installing-yarn">Installing yarn&lt;/h3>
&lt;p>Install yarn using Volta:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta install yarn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Confirm the installation by checking if yarn is listed:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="creating-a-typescript-project">Creating a TypeScript Project&lt;/h3>
&lt;p>Initialize yarn:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">yarn init -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install Node.js:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta pin node@20.0.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install TypeScript:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">yarn add typescript
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install ts-node:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">yarn add --dev ts-node
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Create a tsconfig.json file. This is a TypeScript configuration file that details compilation settings. In this case, you can leave it at the default settings generated:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">yarn tsc --init
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Test a sample program:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;console.log(&amp;#39;Hello, TypeScript!&amp;#39;);&amp;#34;&lt;/span> &amp;gt; hello.ts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yarn ts-node hello.ts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If &amp;#34;Hello, TypeScript!&amp;#34; is displayed, it&amp;#39;s a success.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The test script ran successfully. This completes the setup of the TypeScript development environment.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article described how to set up a TypeScript development environment easily using Volta. By using Volta, managing Node.js versions becomes straightforward, making the development environment setup smoother. Additionally, specifying the Node.js version with Volta in your project&amp;rsquo;s package.json can help eliminate version discrepancies among developers, adding another layer of convenience to your workflow.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/022-typescript-enum/" >Using Enums in TypeScript&lt;/a> (how to use TypeScript Enums)&lt;/li>
&lt;/ul></description></item><item><title>Explaining Capacity and Performance Management in ITIL v4</title><link>https://bossagyu.com/en/blog/020-itilv4-capacity-and-performance-management/</link><pubDate>Tue, 27 Feb 2024 08:53:36 +0900</pubDate><guid>https://bossagyu.com/en/blog/020-itilv4-capacity-and-performance-management/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains capacity and performance management as outlined in ITIL v4. I will also apply what I have understood to my own experiences, explaining the process of capacity and performance management.&lt;/p>
&lt;h2 id="what-are-capacity-and-performance-management">What are Capacity and Performance Management?&lt;/h2>
&lt;p>Capacity and Performance Management involves managing the performance of services and the resources supporting them. The goal is to optimize the performance of services and ensure that the capacity of services is appropriately maintained.&lt;/p>
&lt;h2 id="processes-in-capacity-and-performance-management">Processes in Capacity and Performance Management&lt;/h2>
&lt;p>The processes in capacity and performance management include:&lt;/p>
&lt;ul>
&lt;li>Establishing Capacity and Performance Control&lt;/li>
&lt;li>Analyzing and Improving Service Capacity and Performance&lt;/li>
&lt;/ul>
&lt;h3 id="establishing-capacity-and-performance-control">Establishing Capacity and Performance Control&lt;/h3>
&lt;p>Establishing capacity and performance control involves agreeing with stakeholders on the usage and performance standards for the IT resources used by the service, and deciding on the timing, baseline, and format for evaluation.&lt;/p>
&lt;p>It is realized through the following steps:&lt;/p>
&lt;ol>
&lt;li>Identifying service capacity and performance requirements&lt;/li>
&lt;li>Agreeing on service capacity and performance requirements&lt;/li>
&lt;li>Deciding on capacity and performance requirements&lt;/li>
&lt;li>Designing capacity and performance evaluation metrics and reports&lt;/li>
&lt;/ol>
&lt;p>Applying my experiences to these processes, I understood them as follows:&lt;/p>
&lt;ul>
&lt;li>Identifying service capacity and performance requirements
&lt;ul>
&lt;li>As an internal platform provider, I identified latency performance requirements (99%ile in Nms) demanded by internal users.&lt;/li>
&lt;li>Based on these thresholds, we conducted performance verification and measured throughput per instance.&lt;/li>
&lt;li>We calculated the cost based on the throughput measurements.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Agreeing on service capacity and performance requirements
&lt;ul>
&lt;li>We agreed on latency performance and throughput with stakeholders.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Deciding on capacity and performance requirements
&lt;ul>
&lt;li>This remained unchanged from what was agreed upon.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Designing capacity and performance evaluation metrics and reports
&lt;ul>
&lt;li>We used a tracing tool called Dynatrace to measure and report performance.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="analyzing-and-improving-service-capacity-and-performance">Analyzing and Improving Service Capacity and Performance&lt;/h3>
&lt;p>Analyze issues in usage and performance conditions from service output logs and incident information.&lt;/p>
&lt;p>It is realized through the following steps:&lt;/p>
&lt;ol>
&lt;li>Analyzing capacity and performance&lt;/li>
&lt;li>Reporting capacity and performance&lt;/li>
&lt;li>Planning and designing capacity and performance&lt;/li>
&lt;/ol>
&lt;p>Applying my experiences to these activities, I understood them as follows:&lt;/p>
&lt;ul>
&lt;li>Analyzing capacity and performance
&lt;ul>
&lt;li>We used Dynatrace to analyze latency performance and throughput.&lt;/li>
&lt;li>We identified performance issues from incident information.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Reporting capacity and performance
&lt;ul>
&lt;li>We visualized latency performance and throughput using Dynatrace&amp;rsquo;s dashboard.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Planning and designing capacity and performance
&lt;ul>
&lt;li>Anticipating growth in users, we forecasted that the current capacity would be insufficient.&lt;/li>
&lt;li>We made plans to increase capacity based on demand forecasts and executed them.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>In this article, I explained capacity and performance management based on what I have learned and applied it to my own experiences. I understood that capacity and performance management involves managing aspects similar to availability management, but from the perspective of capacity and performance.
In my experience, discussions about performance and availability often go hand in hand, and I rarely deal with them independently.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/016-itilv4-availability-management/" >Understanding Availability Management in ITIL v4&lt;/a> (related ITIL v4 article)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/018-itilv4-business-analysis/" >Explaining Business Analysis in ITIL v4&lt;/a> (related ITIL v4 article)&lt;/li>
&lt;/ul></description></item><item><title>How to Use Stable Diffusion Web UI on Mac</title><link>https://bossagyu.com/en/blog/019-stable-diffusion/</link><pubDate>Mon, 12 Feb 2024 11:24:59 +0900</pubDate><guid>https://bossagyu.com/en/blog/019-stable-diffusion/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article will guide you through installing Stable Diffusion Web UI on Mac and using it locally.&lt;/p>
&lt;h2 id="what-is-stable-diffusion">What is Stable Diffusion?&lt;/h2>
&lt;p>Stable Diffusion is a type of image processing technology using AI. By inputting text, it can generate images corresponding to that text.&lt;/p>
&lt;h2 id="ways-to-use-stable-diffusion">Ways to Use Stable Diffusion&lt;/h2>
&lt;p>There are two main ways to use Stable Diffusion:&lt;/p>
&lt;ul>
&lt;li>Using web applications like &lt;a class="link" href="https://huggingface.co/" target="_blank" rel="noopener"
>Hugging Face&lt;/a> or &lt;a class="link" href="https://beta.dreamstudio.ai/generate" target="_blank" rel="noopener"
>Dream Studio&lt;/a>&lt;/li>
&lt;li>Using Stable Diffusion Web UI locally&lt;/li>
&lt;/ul>
&lt;p>This article focuses on using Stable Diffusion Web UI locally. While web applications are easy for trial purposes, they might have limitations or costs for generating a significant number of images. Thus, local usage is recommended for more extensive needs.&lt;/p>
&lt;h2 id="how-to-use-stable-diffusion-web-ui-locally">How to Use Stable Diffusion Web UI Locally&lt;/h2>
&lt;p>We will use &lt;a class="link" href="https://github.com/AUTOMATIC1111/stable-diffusion-webui" target="_blank" rel="noopener"
>stable-diffusion-web-ui&lt;/a> published by AUTOMATIC1111 for this purpose.&lt;/p>
&lt;ol>
&lt;li>Prepare the environment&lt;/li>
&lt;li>Install stable-diffusion-web-ui&lt;/li>
&lt;li>Place the model files&lt;/li>
&lt;li>Start stable-diffusion-web-ui and generate images&lt;/li>
&lt;/ol>
&lt;h3 id="1-preparing-the-environment">1. Preparing the Environment&lt;/h3>
&lt;p>First, we need to install Python and other necessary libraries using homebrew.&lt;/p>
&lt;p>Install homebrew:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Set the homebrew path:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">:/opt/homebrew/bin/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install related libraries:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">brew install cmake protobuf rust pyenv git wget
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Set up the Python environment using pyenv. This allows you to use multiple versions of Python. For building the Python environment, refer to &lt;a class="link" href="https://bossagyu.com/blog/004-python-setup/" target="_blank" rel="noopener"
>this article&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv install 3.10.6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv &lt;span class="nb">local&lt;/span> 3.10.6
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-installing-stable-diffusion-web-ui">2. Installing stable-diffusion-web-ui&lt;/h3>
&lt;p>Clone the repository using git clone:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> stable-diffusion-webui
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Set up a virtual environment using venv to keep the environment clean:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python -m venv venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> venv/bin/activate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Now the environment is ready.&lt;/p>
&lt;h3 id="3-placing-the-model-files">3. Placing the Model Files&lt;/h3>
&lt;p>Next, download the model files and place them in the &lt;code>stable-diffusion-webui/models/Stable-diffusion/&lt;/code> directory. Model files can be downloaded from sites like:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://dream-studio.tech/" target="_blank" rel="noopener"
>Civitai&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://huggingface.co/models" target="_blank" rel="noopener"
>Hugging Face&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>For this example, we&amp;rsquo;ll download the &lt;code>blue_pencil&lt;/code> model from Civitai.&lt;/p>
&lt;p>Search for &lt;code>blue_pencil&lt;/code> in Civitai&amp;rsquo;s search bar, select &lt;code>blue_pencil&lt;/code> from the search results, and click the &lt;code>Download&lt;/code> button.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/019-stable-diffusion/img-019-001.png"
width="1357"
height="1019"
srcset="https://bossagyu.com/en/blog/019-stable-diffusion/img-019-001_hu0340b7643e68d89bf045571164e24a86_1170489_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/019-stable-diffusion/img-019-001_hu0340b7643e68d89bf045571164e24a86_1170489_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Download Screen"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="319px"
>&lt;/p>
&lt;p>Move the downloaded model to the directory:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mv ~/Downloads/bluePencilXL_v401.safetensors models/Stable-diffusion/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="4-starting-stable-diffusion-web-ui-and-generating-images">4. Starting stable-diffusion-web-ui and Generating Images&lt;/h3>
&lt;p>Finally, start stable-diffusion-web-ui and generate images:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">./webui.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After launching, input the text in the prompt and generate images:&lt;/p>
&lt;ol>
&lt;li>Select the downloaded &lt;code>blue_pencil&lt;/code> model in Stable Diffusion checkpoint.&lt;/li>
&lt;li>Enter elements you want in the image in the prompt.&lt;/li>
&lt;li>Enter elements you don&amp;rsquo;t want in the image in the Negative prompt.&lt;/li>
&lt;li>Click Generate.&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/019-stable-diffusion/img-019-002.png"
width="3304"
height="1860"
srcset="https://bossagyu.com/en/blog/019-stable-diffusion/img-019-002_hu4a45a04ce22c441948fe1eb974670ec4_1576758_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/019-stable-diffusion/img-019-002_hu4a45a04ce22c441948fe1eb974670ec4_1576758_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Image Generation Screen"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>&lt;/p>
&lt;p>By inputting &amp;ldquo;a dog wearing pink sunglasses,&amp;rdquo; the output matched the input. You can generate various images by inputting different texts.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article introduced how to install and use Stable Diffusion Web UI on Mac locally. Using it locally allows you to generate images freely without the limitations or costs associated with web applications.&lt;/p></description></item><item><title>Explaining Business Analysis in ITIL v4</title><link>https://bossagyu.com/en/blog/018-itilv4-business-analysis/</link><pubDate>Fri, 09 Feb 2024 09:00:56 +0900</pubDate><guid>https://bossagyu.com/en/blog/018-itilv4-business-analysis/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article consolidates my learning and understanding of business analysis as per ITIL v4. I will also apply my own experiences to explain the business analysis process.&lt;/p>
&lt;h2 id="what-is-business-analysis">What is Business Analysis?&lt;/h2>
&lt;p>Business analysis involves analyzing a business or any other component to propose solutions for meeting those needs or solving business challenges. It is important to note that the term &amp;ldquo;business&amp;rdquo; analysis does not solely pertain to business entities.&lt;/p>
&lt;h2 id="examples-of-business-analysis-techniques">Examples of Business Analysis Techniques&lt;/h2>
&lt;p>Examples of business analysis techniques include:&lt;/p>
&lt;ul>
&lt;li>SWOT Analysis&lt;/li>
&lt;li>User Stories&lt;/li>
&lt;/ul>
&lt;p>For specific methods, I defer to other sites as they are not the main focus here. Personally, I often use Customer Journey Maps, although it is not listed as an example here.&lt;/p>
&lt;h2 id="the-business-analysis-process">The Business Analysis Process&lt;/h2>
&lt;p>The business analysis process includes two main processes:&lt;/p>
&lt;ul>
&lt;li>Designing and Maintaining Business Analysis Approaches&lt;/li>
&lt;li>Identifying Business Analysis and Solutions&lt;/li>
&lt;/ul>
&lt;h3 id="designing-and-maintaining-business-analysis-approaches">Designing and Maintaining Business Analysis Approaches&lt;/h3>
&lt;p>This process focuses on establishing a consistent and effective approach to business analysis by addressing the current and anticipated needs of the organization. It is executed as follows:&lt;/p>
&lt;ul>
&lt;li>Analyzing the organization and requirements&lt;/li>
&lt;li>Reviewing business analysis approach methodologies&lt;/li>
&lt;li>Implementing the business analysis approach&lt;/li>
&lt;/ul>
&lt;p>In my organization, we rarely conduct analysis at the business layer level. Generally, this process involves analyzing specific requirements using certain methodologies.&lt;/p>
&lt;h3 id="identifying-business-analysis-and-solutions">Identifying Business Analysis and Solutions&lt;/h3>
&lt;p>This process emphasizes analyzing stakeholders&amp;rsquo; needs and requirements. It includes identifying and proposing solutions to address the stakeholders&amp;rsquo; needs and requirements. It is executed as follows:&lt;/p>
&lt;ol>
&lt;li>Collecting and analyzing information from stakeholders&lt;/li>
&lt;li>Defining solution options and identifying recommended solutions&lt;/li>
&lt;li>Providing support to the solution delivery team&lt;/li>
&lt;li>Evaluating and assessing the performance of the solution&lt;/li>
&lt;/ol>
&lt;p>Applying my own experiences to these activities, I understood them as follows:&lt;/p>
&lt;h4 id="for-12">For 1,2&lt;/h4>
&lt;ul>
&lt;li>Collecting information from stakeholders and conducting analysis.&lt;/li>
&lt;li>Identifying the issues to be solved (why) from the analysis results and determining what solutions are available (what).&lt;/li>
&lt;/ul>
&lt;h4 id="for-3">For 3&lt;/h4>
&lt;ul>
&lt;li>Considering how to solve the identified why and what with the product team.&lt;/li>
&lt;li>Deciding on the resolution method and evaluation criteria to determine how success will be measured.&lt;/li>
&lt;/ul>
&lt;h4 id="for-4">For 4&lt;/h4>
&lt;ul>
&lt;li>Regularly evaluating how well the solution meets the established criteria.&lt;/li>
&lt;li>I recommend automating data collection and visualizing it with tools like Grafana for visibility.&lt;/li>
&lt;/ul>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article explained business analysis based on my learning and experiences. Personally, I&amp;rsquo;ve understood that business analysis does not only target businesses and includes processes beyond analysis.&lt;/p>
&lt;h2 id="reference">Reference&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://www.axelos.com/resource-hub/practice/business-analysis-management-itil-4-practice-guide" target="_blank" rel="noopener"
>Business analysis management: ITIL 4 Practice Guide&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/016-itilv4-availability-management/" >Understanding Availability Management in ITIL v4&lt;/a> (related ITIL v4 article)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/020-itilv4-capacity-and-performance-management/" >Explaining Capacity and Performance Management in ITIL v4&lt;/a> (related ITIL v4 article)&lt;/li>
&lt;/ul></description></item><item><title>Complete Guide to Using GitHub Copilot in VSCode</title><link>https://bossagyu.com/en/blog/017-vscode-copilot/</link><pubDate>Sun, 04 Feb 2024 22:34:51 +0900</pubDate><guid>https://bossagyu.com/en/blog/017-vscode-copilot/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article comprehensively covers how to set up GitHub Copilot in VSCode, enable it for Markdown files, and use Chat tools. A GitHub Copilot account is required as a prerequisite.&lt;/p>
&lt;h2 id="getting-started-with-github-copilot-in-vscode">Getting Started with GitHub Copilot in VSCode&lt;/h2>
&lt;h3 id="installing-the-extension">Installing the Extension&lt;/h3>
&lt;p>First, you need to install the extension in VSCode.
Open VSCode, click on the icon with four squares in the left menu, enter &amp;ldquo;copilot&amp;rdquo; in the search text input, and click &amp;ldquo;install&amp;rdquo; to start the installation.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/017-vscode-copilot/img-017-001.png"
width="626"
height="267"
srcset="https://bossagyu.com/en/blog/017-vscode-copilot/img-017-001_hua2075257b737ae41d41cddc9273959b8_30520_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/017-vscode-copilot/img-017-001_hua2075257b737ae41d41cddc9273959b8_30520_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Extension"
class="gallery-image"
data-flex-grow="234"
data-flex-basis="562px"
>&lt;/p>
&lt;h3 id="linking-with-github">Linking with GitHub&lt;/h3>
&lt;p>After clicking install and completing the installation, the following screen will appear. Click on &amp;ldquo;Sign in to GitHub.&amp;rdquo;&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/017-vscode-copilot/img-017-002.png"
width="574"
height="141"
srcset="https://bossagyu.com/en/blog/017-vscode-copilot/img-017-002_hud50fff9149b461526033d19f8d6aa177_7541_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/017-vscode-copilot/img-017-002_hud50fff9149b461526033d19f8d6aa177_7541_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Sing in to GitHub Screen"
class="gallery-image"
data-flex-grow="407"
data-flex-basis="977px"
>&lt;/p>
&lt;p>You will be asked to allow access to your GitHub account. Click &amp;ldquo;Allow&amp;rdquo; to grant permission.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/017-vscode-copilot/img-017-003.png"
width="984"
height="245"
srcset="https://bossagyu.com/en/blog/017-vscode-copilot/img-017-003_hu2ee054375fc09dad64838e766feb723e_62679_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/017-vscode-copilot/img-017-003_hu2ee054375fc09dad64838e766feb723e_62679_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Access Permission Screen"
class="gallery-image"
data-flex-grow="401"
data-flex-basis="963px"
>&lt;/p>
&lt;p>Click &amp;ldquo;Authorize Visual Studio Code&amp;rdquo; to give permission.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/017-vscode-copilot/img-017-004.png"
width="653"
height="735"
srcset="https://bossagyu.com/en/blog/017-vscode-copilot/img-017-004_hu681f1d2b9e85acefcddbdd060977aa52_52159_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/017-vscode-copilot/img-017-004_hu681f1d2b9e85acefcddbdd060977aa52_52159_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Confirmation Screen"
class="gallery-image"
data-flex-grow="88"
data-flex-basis="213px"
>&lt;/p>
&lt;p>This completes the integration of GitHub Copilot with VSCode, and you&amp;rsquo;re now ready to use it.&lt;/p>
&lt;h2 id="how-to-use">How to Use&lt;/h2>
&lt;p>Basically, as you write code, suggestions will automatically appear.
Use the following commands to write code efficiently with the suggested completions.&lt;/p>
&lt;h3 id="cheat-sheet">Cheat Sheet&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Function&lt;/th>
&lt;th>Key&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Accept suggestion&lt;/td>
&lt;td>Tab&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Reject suggestion&lt;/td>
&lt;td>Esc&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Open Copilot&lt;/td>
&lt;td>Ctrl + Enter&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Next suggestion&lt;/td>
&lt;td>Alt/Option + ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Previous suggestion&lt;/td>
&lt;td>Alt/Option + [&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Trigger inline Copilot&lt;/td>
&lt;td>Alt/Option + \&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="enabling-completions-for-markdown-files">Enabling Completions for Markdown Files&lt;/h2>
&lt;p>By default, GitHub Copilot has completions disabled for Markdown files.
If you write blogs or documentation in Markdown, enabling this feature is quite useful.&lt;/p>
&lt;h3 id="how-to-enable">How to Enable&lt;/h3>
&lt;ol>
&lt;li>Open the GitHub Copilot plugin page in VSCode&lt;/li>
&lt;li>Click the gear icon to open settings&lt;/li>
&lt;li>Change &lt;code>markdown&lt;/code> from &lt;code>false&lt;/code> to &lt;code>true&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>This enables Copilot completions in Markdown files.
This blog was also written with GitHub Copilot assistance, significantly improving efficiency.&lt;/p>
&lt;h2 id="using-chat-tools-for-even-more-power">Using Chat Tools for Even More Power&lt;/h2>
&lt;p>GitHub Copilot Chat has a feature called &amp;ldquo;Chat tools&amp;rdquo; that lets you execute various tasks within the chat.&lt;/p>
&lt;h3 id="how-to-use-chat-tools">How to Use Chat Tools&lt;/h3>
&lt;p>Simply enter commands in the format &lt;code>#&amp;lt;command&amp;gt; &amp;lt;args&amp;gt;&lt;/code>.&lt;/p>
&lt;h3 id="common-chat-tool-commands">Common Chat Tool Commands&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Command&lt;/th>
&lt;th>Function&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>#codebase&lt;/code>&lt;/td>
&lt;td>Search the entire current workspace&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>#selection&lt;/code>&lt;/td>
&lt;td>Add currently selected code in the editor as context&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>#terminal_selection&lt;/code>&lt;/td>
&lt;td>Add currently selected terminal output as context. Useful for referencing error output.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>#fetch_webpage&lt;/code>&lt;/td>
&lt;td>Fetch content from a URL and add it as context&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For more useful commands, check the official documentation:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://code.visualstudio.com/docs/copilot/reference/copilot-vscode-features#_chat-tools" target="_blank" rel="noopener"
>Github Copilot Chat tools&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article covered how to set up and use GitHub Copilot in VSCode.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Basic Setup&lt;/strong>: Extension installation and GitHub integration&lt;/li>
&lt;li>&lt;strong>Shortcuts&lt;/strong>: Tab/Esc to accept/reject suggestions&lt;/li>
&lt;li>&lt;strong>Markdown Support&lt;/strong>: Change markdown setting to true&lt;/li>
&lt;li>&lt;strong>Chat Tools&lt;/strong>: Add context with &lt;code>#codebase&lt;/code> and &lt;code>#selection&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>GitHub Copilot not only provides code completions but also assists with writing text.
Give it a try and see how it can help you.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/005-github-copilot/" >How to Use GitHub Copilot in IntelliJ&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/035-github-copilot-markdown/" >How to Enable GitHub Copilot for Markdown&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/042-github-copilot/" >How to Use GitHub Copilot More Effectively with Chat Tools&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/043-codex/" >How to Use Codex CLI&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Understanding Availability Management in ITIL v4</title><link>https://bossagyu.com/en/blog/016-itilv4-availability-management/</link><pubDate>Tue, 30 Jan 2024 20:34:58 +0900</pubDate><guid>https://bossagyu.com/en/blog/016-itilv4-availability-management/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains availability management as defined in ITIL v4. I will also relate my own experiences to explain the processes involved in availability management.&lt;/p>
&lt;h2 id="what-is-availability-management">What is Availability Management?&lt;/h2>
&lt;p>Availability management involves activities to ensure the availability of a service. The objective of availability management is to ensure that the service delivers an agreed level of availability to meet the needs of customers and users.&lt;/p>
&lt;h2 id="processes-in-availability-management">Processes in Availability Management&lt;/h2>
&lt;p>There are two main processes in availability management:&lt;/p>
&lt;ul>
&lt;li>Establishing Service Availability Control&lt;/li>
&lt;li>Analyzing and Improving Service Availability&lt;/li>
&lt;/ul>
&lt;h3 id="establishing-service-availability-control">Establishing Service Availability Control&lt;/h3>
&lt;p>Establishing service availability control ensures the availability of a service. It is realized through the following steps:&lt;/p>
&lt;ol>
&lt;li>Identifying service availability requirements&lt;/li>
&lt;li>Agreeing on service availability requirements&lt;/li>
&lt;li>Deciding on availability measurement requirements&lt;/li>
&lt;li>Designing availability metrics and reporting&lt;/li>
&lt;/ol>
&lt;p>Applying my own experiences to these processes, I understood them as follows:&lt;/p>
&lt;ul>
&lt;li>Identifying service availability requirements
&lt;ul>
&lt;li>Identifying the types of users and the business risks of service downtime.&lt;/li>
&lt;li>For my service, which is an internal platform, I identified the impacts on various services using the platform.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Agreeing on service availability requirements
&lt;ul>
&lt;li>Agreeing on the service&amp;rsquo;s availability (e.g., 99% uptime) in the form of an SLA.&lt;/li>
&lt;li>We also clarified downtime criteria and exceptions for uptime calculations.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Deciding on availability measurement requirements
&lt;ul>
&lt;li>As the availability requirements were agreed upon earlier, there were no specific measurement requirements.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Designing availability metrics and reporting
&lt;ul>
&lt;li>Primarily designed around the &amp;lsquo;downtime/uptime&amp;rsquo; formula.&lt;/li>
&lt;li>For reporting, we created a dashboard to visualize the availability metrics.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="analyzing-and-improving-service-availability">Analyzing and Improving Service Availability&lt;/h3>
&lt;p>This process, as the name suggests, involves analyzing and improving service availability. It is realized through the following steps:&lt;/p>
&lt;ol>
&lt;li>Analyzing service availability&lt;/li>
&lt;li>Reporting on service availability&lt;/li>
&lt;li>Planning and designing for service availability&lt;/li>
&lt;/ol>
&lt;p>Applying my own experiences to these processes, I understood them as follows:&lt;/p>
&lt;ul>
&lt;li>Analyzing service availability
&lt;ul>
&lt;li>Confirming that service availability is being achieved and compiling the data.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Reporting on service availability
&lt;ul>
&lt;li>Reflecting availability on a dashboard that is accessible to everyone.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Planning and designing for service availability
&lt;ul>
&lt;li>In case of incidents that affect availability, we formulated plans for prevention of recurrence.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="reference">Reference&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://www.axelos.com/resource-hub/practice/availability-management-itil-4-practice-guide" target="_blank" rel="noopener"
>Availability management: ITIL 4 Practice Guide&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/018-itilv4-business-analysis/" >Explaining Business Analysis in ITIL v4&lt;/a> (related ITIL v4 article)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/020-itilv4-capacity-and-performance-management/" >Explaining Capacity and Performance Management in ITIL v4&lt;/a> (related ITIL v4 article)&lt;/li>
&lt;/ul></description></item><item><title>How to Check if an S3 Object Exists in Python boto3</title><link>https://bossagyu.com/en/blog/015-s3-object-check/</link><pubDate>Sat, 27 Jan 2024 21:41:37 +0900</pubDate><guid>https://bossagyu.com/en/blog/015-s3-object-check/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to check whether an S3 object exists using Python.
boto3 provides two API levels: &lt;code>resource&lt;/code> and &lt;code>client&lt;/code>. I&amp;rsquo;ll cover both methods.&lt;/p>
&lt;p>I used this approach when developing a LINE Bot where I needed to check if a user&amp;rsquo;s configuration file existed in S3.&lt;/p>
&lt;h2 id="resource-vs-client-which-should-you-use">resource vs client: Which Should You Use?&lt;/h2>
&lt;p>boto3 has two API levels:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>API&lt;/th>
&lt;th>Characteristics&lt;/th>
&lt;th>Best For&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>resource&lt;/code>&lt;/td>
&lt;td>High-level API, object-oriented&lt;/td>
&lt;td>Simple operations, readability&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>client&lt;/code>&lt;/td>
&lt;td>Low-level API, closer to AWS API&lt;/td>
&lt;td>Fine-grained control, performance&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Conclusion&lt;/strong>: For simple existence checks, I recommend using &lt;code>client&lt;/code>&amp;rsquo;s &lt;code>head_object&lt;/code>. Here&amp;rsquo;s why:&lt;/p>
&lt;ul>
&lt;li>&lt;code>head_object&lt;/code> only retrieves object metadata, making it lightweight&lt;/li>
&lt;li>&lt;code>resource&lt;/code>&amp;rsquo;s &lt;code>load()&lt;/code> internally calls &lt;code>head_object&lt;/code> anyway&lt;/li>
&lt;li>&lt;code>client&lt;/code> is closer to the AWS API with more predictable behavior&lt;/li>
&lt;/ul>
&lt;h2 id="using-boto3client-recommended">Using boto3.client (Recommended)&lt;/h2>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">boto3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">botocore.exceptions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ClientError&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">check_s3_object_exists&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">object_key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Check if an S3 object exists&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">boto3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;s3&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">head_object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Bucket&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">object_key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="n">ClientError&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">error_code&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Error&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;Code&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">error_code&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;404&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Re-raise non-404 errors (e.g., permission issues)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Usage example&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">check_s3_object_exists&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;my-bucket&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;path/to/file.json&amp;#39;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Object exists&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Object does not exist&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="key-points">Key Points&lt;/h3>
&lt;ul>
&lt;li>&lt;code>head_object&lt;/code> retrieves only metadata without downloading the object content&lt;/li>
&lt;li>Non-404 errors (e.g., 403: Access Denied) should be re-raised for proper handling&lt;/li>
&lt;li>Wrapping in a function makes it easier to test&lt;/li>
&lt;/ul>
&lt;h2 id="using-boto3resource">Using boto3.resource&lt;/h2>
&lt;p>If you prefer an object-oriented approach, this method also works:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">boto3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">botocore.exceptions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ClientError&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">check_s3_object_exists_resource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">object_key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Check if an S3 object exists (resource version)&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">boto3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;s3&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">object_key&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="n">ClientError&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">error_code&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Error&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;Code&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">error_code&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;404&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="common-pitfalls">Common Pitfalls&lt;/h2>
&lt;h3 id="1-confusing-permission-errors-with-404">1. Confusing Permission Errors with 404&lt;/h3>
&lt;p>If you lack permissions to access the S3 bucket, you&amp;rsquo;ll get a 403 error.
Catching only 404 and treating it as &amp;ldquo;doesn&amp;rsquo;t exist&amp;rdquo; will mask permission issues.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># BAD: Swallowing all errors&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="n">ClientError&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span> &lt;span class="c1"># Permission errors also treated as &amp;#34;doesn&amp;#39;t exist&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># GOOD: Re-raise non-404 errors&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="n">ClientError&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Error&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;Code&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;404&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="c1"># Let caller handle permission errors&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-using-list_objects-is-inefficient">2. Using list_objects is Inefficient&lt;/h3>
&lt;p>You could use &lt;code>list_objects&lt;/code> with prefix search, but it becomes slow with many objects.
For single object existence checks, use &lt;code>head_object&lt;/code>.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;ul>
&lt;li>Use &lt;code>head_object&lt;/code> to check S3 object existence&lt;/li>
&lt;li>Handle non-404 errors properly (they may indicate permission issues)&lt;/li>
&lt;li>Both &lt;code>resource&lt;/code> and &lt;code>client&lt;/code> work, but &lt;code>client&lt;/code> is simpler&lt;/li>
&lt;/ul>
&lt;h2 id="references">References&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/head_object.html" target="_blank" rel="noopener"
>boto3 S3 head_object&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://stackoverflow.com/questions/33842944/check-if-a-key-exists-in-a-bucket-in-s3-using-boto3" target="_blank" rel="noopener"
>Stack Overflow: check if a file exists in s3 bucket using boto3&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/046-clean-bot-technical/" >Building a Cleaning Reminder Bot with AWS Lambda + LINE&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/004-python-setup/" >Setting Up a Local Environment Using Pyenv and venv&lt;/a> (Python environment setup)&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/032-python-uv/" >Setting Up a Python Development Environment on Mac with UV&lt;/a> (modern Python environment setup)&lt;/li>
&lt;/ul></description></item><item><title>Integrating AWS API Gateway with Lambda</title><link>https://bossagyu.com/en/blog/014-aws-apigateway-lambda/</link><pubDate>Sat, 13 Jan 2024 18:06:52 +0900</pubDate><guid>https://bossagyu.com/en/blog/014-aws-apigateway-lambda/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Integrating AWS API Gateway with Lambda enables you to call Lambda functions from API Gateway. This article introduces how to integrate AWS API Gateway with Lambda.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>It is assumed that the Lambda function has already been created. If not, please refer to the following article for creation:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/getting-started.html" target="_blank" rel="noopener"
>Creating an AWS Lambda function&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="deciding-how-to-integrate-api-gateway-with-lambda">Deciding How to Integrate API Gateway with Lambda&lt;/h2>
&lt;p>When integrating API Gateway with Lambda, you need to consider the following two points:&lt;/p>
&lt;ol>
&lt;li>The request format for API Gateway&lt;/li>
&lt;li>Whether to use Proxy Integration or Non-Proxy Integration&lt;/li>
&lt;/ol>
&lt;h3 id="request-format-for-api-gateway">Request Format for API Gateway&lt;/h3>
&lt;p>You can choose from the following formats:&lt;/p>
&lt;ul>
&lt;li>REST API&lt;/li>
&lt;li>HTTP API&lt;/li>
&lt;li>WebSocket API&lt;/li>
&lt;/ul>
&lt;p>If you choose to use the REST API format, you will need to decide between REST API and HTTP API. While REST API has more features, it is more expensive than HTTP API. HTTP API is a good choice for simpler requirements.&lt;/p>
&lt;p>For a detailed comparison, please refer to the &lt;a class="link" href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html" target="_blank" rel="noopener"
>official documentation&lt;/a>.&lt;/p>
&lt;h2 id="proxy-vs-non-proxy-integration">Proxy vs. Non-Proxy Integration&lt;/h2>
&lt;p>Using Proxy Integration standardizes the format of the response returned from Lambda. It is generally recommended to use Proxy Integration.&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html" target="_blank" rel="noopener"
>Setting up Lambda Proxy Integrations in API Gateway&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="configuration">Configuration&lt;/h2>
&lt;p>After creating the Lambda function, select &amp;ldquo;Add Trigger.&amp;rdquo;&lt;br>
&lt;img src="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-001.png"
width="3298"
height="980"
srcset="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-001_hu7a602e3d547dfd28cd38c53d38cddce0_223431_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-001_hu7a602e3d547dfd28cd38c53d38cddce0_223431_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Add Lambda Trigger"
class="gallery-image"
data-flex-grow="336"
data-flex-basis="807px"
>&lt;/p>
&lt;p>Choose API Gateway.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-002.png"
width="966"
height="522"
srcset="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-002_hu446ae81980eb6a0d259b3af0f9d88117_59141_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-002_hu446ae81980eb6a0d259b3af0f9d88117_59141_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Select API Gateway"
class="gallery-image"
data-flex-grow="185"
data-flex-basis="444px"
>&lt;/p>
&lt;p>Configure the trigger addition as shown below.
&lt;img src="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-003.png"
width="892"
height="1203"
srcset="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-003_hu45d67fc282f3323f6fa435777af50650_166604_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-003_hu45d67fc282f3323f6fa435777af50650_166604_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="API Gateway Configuration"
class="gallery-image"
data-flex-grow="74"
data-flex-basis="177px"
>&lt;/p>
&lt;p>Once configured successfully, the screen should look like this.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-004.png"
width="1396"
height="969"
srcset="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-004_hu4410e1ba85bd0e88e16bb309c05ab865_175168_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/014-aws-apigateway-lambda/img-014-004_hu4410e1ba85bd0e88e16bb309c05ab865_175168_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="API Gateway Configuration Complete"
class="gallery-image"
data-flex-grow="144"
data-flex-basis="345px"
>&lt;/p>
&lt;p>Access the &lt;code>API endpoint&lt;/code> listed with a tool like curl to execute the Lambda function.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ curl https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/default/apigateway-get-sample
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;Hello from Lambda!&amp;#34;&lt;/span>%
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article introduced how to integrate AWS API Gateway with Lambda. By integrating with API Gateway, you can externally invoke Lambda functions at any time.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/006-intellij-lamda-setup/" >Efficient Lambda Development with AWS Toolkit in IntelliJ&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/008-aws-eventbrdge/" >How to Schedule Lambda Functions with AWS EventBridge&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/046-clean-bot-technical/" >Building a Cleaning Reminder Bot with AWS Lambda + LINE&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Create a Product Strategy</title><link>https://bossagyu.com/en/blog/013-good-strategy-bad-strategy/</link><pubDate>Mon, 08 Jan 2024 21:55:15 +0900</pubDate><guid>https://bossagyu.com/en/blog/013-good-strategy-bad-strategy/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article summarizes how to create a good product strategy, based on the book &amp;ldquo;Good Strategy Bad Strategy.&amp;rdquo;&lt;/p>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>As a product owner in my job, I took over a product from the previous owner. The product lacked a clear strategy and direction, prompting me to develop a new product strategy.&lt;/p>
&lt;p>While developing the strategy, I realized that the term &amp;ldquo;strategy&amp;rdquo; is interpreted in various ways by different people, often used loosely in many contexts. To understand what constitutes a good strategy, I referred to &amp;ldquo;Good Strategy Bad Strategy.&amp;rdquo;&lt;/p>
&lt;h2 id="what-is-a-good-strategy">What is a Good Strategy?&lt;/h2>
&lt;p>A good strategy identifies critical points where concerted efforts can significantly boost the effect of one’s actions. A strategy should show the direction for an organization to move forward.&lt;/p>
&lt;p>A good strategy has three basic structures:&lt;/p>
&lt;ul>
&lt;li>Diagnosis&lt;/li>
&lt;li>Guiding Policy&lt;/li>
&lt;li>Actions&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/013-good-strategy-bad-strategy/img-013-001-en.png"
width="1798"
height="774"
srcset="https://bossagyu.com/en/blog/013-good-strategy-bad-strategy/img-013-001-en_hu680e7b02c44958a4bc711ee8c4490ebe_136397_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/013-good-strategy-bad-strategy/img-013-001-en_hu680e7b02c44958a4bc711ee8c4490ebe_136397_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Good Strategy&amp;rsquo;s Three Basic Structures"
class="gallery-image"
data-flex-grow="232"
data-flex-basis="557px"
>&lt;/p>
&lt;h3 id="diagnosis">Diagnosis&lt;/h3>
&lt;p>Diagnosis involves assessing the situation to identify the key challenges to address. A good diagnosis separates crucial issues from the complex mix of problems and simplifies them.&lt;/p>
&lt;p>Most of the strategy work lies in figuring out what is happening. Gathering information is crucial. Although the book criticizes consultants&amp;rsquo; frameworks, I find them useful in organizing information after thorough collection.&lt;/p>
&lt;p>In creating my product strategy, I used SWOT analysis and Impact Mapping to organize the current situation and the impact of existing strategies.&lt;/p>
&lt;p>For more on these methods, refer to the following resources:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://www.innovation.co.jp/urumo/swot/" target="_blank" rel="noopener"
>SWOT Analysis&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://amzn.asia/d/0FygsZ4" target="_blank" rel="noopener"
>Impact Mapping&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="guiding-policy">Guiding Policy&lt;/h3>
&lt;p>The guiding policy outlines how to approach the challenges identified in the diagnosis. A good policy is not about goals or visions but about how to face challenges and exclude other options. It focuses efforts on a decisive point to achieve a significant effect.&lt;/p>
&lt;p>A good strategy clearly shows where resources will be allocated according to the strategy.&lt;/p>
&lt;p>In my case, I defined the product direction based on the current situation and vision, focusing on specific target segments and values to offer.&lt;/p>
&lt;h3 id="actions">Actions&lt;/h3>
&lt;p>Actions are a coherent set of steps designed to execute the guiding policy. A strategy coordinates all actions to implement the policy effectively.&lt;/p>
&lt;p>A good strategy includes guidelines for implementing actions.&lt;/p>
&lt;h3 id="my-strategy">My Strategy&lt;/h3>
&lt;p>Based on the above, I developed the following strategy (partially obscured as it was for my company):&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Reduce the new user cost of using xx function.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This simple strategy meets the three basic structures of a good strategy:&lt;/p>
&lt;ul>
&lt;li>Diagnosis
&lt;ul>
&lt;li>Identified increasing new users of xx as critical for the company&amp;rsquo;s benefit.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Guiding Policy
&lt;ul>
&lt;li>Decided to lower the onboarding cost to acquire new users.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Actions
&lt;ul>
&lt;li>Prioritized several approaches to realize the policy.&lt;/li>
&lt;li>Although it could have included more action-oriented words, I decided on the above.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="what-is-a-bad-strategy">What is a Bad Strategy&lt;/h2>
&lt;p>Finally, let&amp;rsquo;s touch on common patterns of bad strategies:&lt;/p>
&lt;p>Characteristics of a Bad Strategy:&lt;/p>
&lt;ul>
&lt;li>Vague
&lt;ul>
&lt;li>Uses jargon or industry terms to obscure simple facts.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Avoids Significant Problems
&lt;ul>
&lt;li>Strategy should overcome difficult challenges and obstacles.&lt;/li>
&lt;li>Strategies focusing only on attainability are bad.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Confuses Goals with Strategy
&lt;ul>
&lt;li>For instance, a 10% revenue increase is a goal, not a strategy.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Sets Wrong Strategic Objectives
&lt;ul>
&lt;li>Strategies set without sufficient investigation of causes and surroundings.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article summarized how to create a good product strategy based on &amp;ldquo;Good Strategy Bad Strategy.&amp;rdquo; Creating a good strategy might not happen at once. However, not having a strategy is like running blindly, unable to judge whether actions are successful or not.&lt;/p>
&lt;p>It&amp;rsquo;s important to start with a basic strategy, constantly observe surroundings, and update the strategy to define the direction for your product and organization.&lt;/p></description></item><item><title>Setting Up Twitter Social Cards</title><link>https://bossagyu.com/en/blog/012-social-card/</link><pubDate>Sat, 06 Jan 2024 21:45:12 +0900</pubDate><guid>https://bossagyu.com/en/blog/012-social-card/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to set up Twitter Social Cards for a blog created with Hugo.&lt;/p>
&lt;h2 id="what-is-a-twitter-social-card">What is a Twitter Social Card?&lt;/h2>
&lt;p>A Twitter Social Card is an image that is displayed when an article is shared on Twitter. The image below is an example of a Twitter Social Card.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/012-social-card/img-012-001.png"
width="602"
height="220"
srcset="https://bossagyu.com/en/blog/012-social-card/img-012-001_hua2e3d73bb1829d9bc1818e9b476a1657_53764_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/012-social-card/img-012-001_hua2e3d73bb1829d9bc1818e9b476a1657_53764_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Twitter Social Card"
class="gallery-image"
data-flex-grow="273"
data-flex-basis="656px"
>&lt;/p>
&lt;p>There are several types of Twitter Social Cards, including:&lt;/p>
&lt;ul>
&lt;li>Summary Card&lt;/li>
&lt;li>Summary Card with Large Image&lt;/li>
&lt;li>App Card&lt;/li>
&lt;li>Player Card&lt;/li>
&lt;/ul>
&lt;p>For sharing blog posts, the most commonly used types are Summary Card and Summary Card with Large Image.&lt;/p>
&lt;p>For more details on each card type, refer to Twitter&amp;rsquo;s &lt;a class="link" href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards" target="_blank" rel="noopener"
>official documentation&lt;/a>.&lt;/p>
&lt;h2 id="how-to-set-up-twitter-social-cards">How to Set Up Twitter Social Cards&lt;/h2>
&lt;p>There are two main methods to set up Twitter Social Cards:&lt;/p>
&lt;ul>
&lt;li>Setting up through the theme&lt;/li>
&lt;li>Setting up independently of the theme&lt;/li>
&lt;/ul>
&lt;h3 id="setting-up-through-the-theme">Setting Up Through the Theme&lt;/h3>
&lt;p>Some themes allow you to set up Twitter Social Cards directly.&lt;/p>
&lt;p>For this example, I&amp;rsquo;ll use the &lt;a class="link" href="https://themes.gohugo.io/hugo-theme-stack/" target="_blank" rel="noopener"
>Stack&lt;/a> theme that I&amp;rsquo;m using. In Stack, you can configure Twitter Social Cards in &lt;code>config.toml&lt;/code> as follows:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">opengraph&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">twitter&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">site&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">card&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;summary&amp;#34;&lt;/span> &lt;span class="c"># summary or summary_large_image&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">defaultImage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">opengraph&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">enabled&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">local&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">src&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;/images/share.webp&amp;#34;&lt;/span> &lt;span class="c"># Path to the default image you want to set&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="setting-up-independently-of-the-theme">Setting Up Independently of the Theme&lt;/h3>
&lt;p>If your theme doesn’t support Twitter Social Card settings, you will need to implement it yourself.&lt;/p>
&lt;p>Hugo’s official &lt;a class="link" href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/twitter_cards.html" target="_blank" rel="noopener"
>template for implementation&lt;/a> is available, which you can use for an easy setup.&lt;/p>
&lt;h2 id="troubleshooting">Troubleshooting&lt;/h2>
&lt;p>If the settings don’t seem to work, it might be due to incorrect implementation or the meta tags not being properly set. In such cases, use the &lt;a class="link" href="https://cards-dev.twitter.com/validator" target="_blank" rel="noopener"
>debugging tool&lt;/a> provided by Twitter to check if the settings have been correctly applied.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article explained how to set up Twitter Social Cards for a blog created with Hugo. Setting up Social Cards can enhance the visibility of your shared articles on Twitter, potentially attracting more readers, so it&amp;rsquo;s definitely worth doing.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/001-hugo-netlify-build/" >Publishing a Blog with Hugo + Netlify + Github&lt;/a> - Blog initial setup&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/007-google-search-console/" >Using Google Search Console to Make Your Blog Searchable on Google&lt;/a> - SEO setup&lt;/li>
&lt;/ul></description></item><item><title>Using ChatGPT to Make a Hugo Blog Multilingual</title><link>https://bossagyu.com/en/blog/011-hugo-multilingul-support/</link><pubDate>Sun, 31 Dec 2023 20:46:36 +0900</pubDate><guid>https://bossagyu.com/en/blog/011-hugo-multilingul-support/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to use ChatGPT to make a blog created with Hugo multilingual.&lt;/p>
&lt;h2 id="translating-articles-into-english-with-chatgpt">Translating Articles into English with ChatGPT&lt;/h2>
&lt;p>You can translate articles written in Markdown into English using &lt;a class="link" href="https://chat.openai.com/" target="_blank" rel="noopener"
>ChatGPT&lt;/a>. When doing so, use the following prompt to ensure that the format remains intact:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Please translate this Markdown into English without altering its format.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Ensure that no extraneous output is included.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Present the translated content in a format that can be easily copied.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Paste your article in Japanese, and ChatGPT will output the translated Markdown. You can copy the output directly by clicking the copy button at the bottom left of the output.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/011-hugo-multilingul-support/img-011-001.png"
width="1542"
height="516"
srcset="https://bossagyu.com/en/blog/011-hugo-multilingul-support/img-011-001_hu712801eb1dd59e23992097ade27112d6_94833_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/011-hugo-multilingul-support/img-011-001_hu712801eb1dd59e23992097ade27112d6_94833_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="ChatGPT Screen"
class="gallery-image"
data-flex-grow="298"
data-flex-basis="717px"
>&lt;/p>
&lt;p>It is highly recommended to use GPT-4 instead of GPT-3.5, despite the subscription cost. GPT-4 significantly outperforms GPT-3.5 in providing accurate responses, making it useful for purposes beyond just translation.&lt;/p>
&lt;h2 id="making-hugo-multilingual">Making Hugo Multilingual&lt;/h2>
&lt;p>Here are the steps to make your Hugo site multilingual.&lt;/p>
&lt;h3 id="creating-configuration-files">Creating Configuration Files&lt;/h3>
&lt;p>Add the following settings to your config.toml:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Set the default language, without this setting English is assumed default.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">defaultContentLanguage&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;jp&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c"># Set configurations for each language&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jp&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Bossagyu Blog&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">languageName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;ja-jp 🇯🇵&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">LanguageCode&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;ja-jp&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">contentDir&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span> &lt;span class="c"># Directory for Japanese blog articles&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">en&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Bossagyu Blog&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">languageName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;en-US 🇺🇸&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">LanguageCode&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;en-US&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">contentDir&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;content.en&amp;#34;&lt;/span> &lt;span class="c"># Directory for English blog articles&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">en&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>With the above settings, you can write Japanese articles in the &lt;code>content&lt;/code> directory and English articles in the &lt;code>content.en&lt;/code> directory to support multiple languages.&lt;/p>
&lt;p>The final directory structure will look like this:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">project/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── content/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── index.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── blog/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── index.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── article1.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── content.en/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── index.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── blog/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── index.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── article1.en.md
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In the English directory, naming files as &lt;code>article-name.en.md&lt;/code> identifies them as English versions of the default language articles, and language switch icons will be added to the articles. For the articles, just copy and paste the translations from ChatGPT.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article explained how to make a Hugo blog multilingual using ChatGPT. Utilizing ChatGPT simplifies the translation process. Multilingual support can help reach audiences outside Japan, increasing readership.&lt;/p>
&lt;p>Since it&amp;rsquo;s low-cost and efficient, it&amp;rsquo;s worth giving it a try.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/001-hugo-netlify-build/" >Publishing a Blog with Hugo + Netlify + Github&lt;/a> - Blog initial setup&lt;/li>
&lt;/ul></description></item><item><title>Creating and Displaying a Favicon with Hugo</title><link>https://bossagyu.com/en/blog/010-favicon/</link><pubDate>Sun, 24 Dec 2023 22:14:39 +0900</pubDate><guid>https://bossagyu.com/en/blog/010-favicon/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains a simple method to create a favicon. It also covers how to display a favicon in Hugo.&lt;/p>
&lt;h2 id="what-is-a-favicon">What is a Favicon?&lt;/h2>
&lt;p>A favicon is an icon that appears in bookmarks, tabs, and home screens for websites. Google has published &lt;a class="link" href="https://developers.google.com/search/docs/appearance/favicon-in-search?hl=ja#guidelines" target="_blank" rel="noopener"
>guidelines for favicons&lt;/a> that appear in search results. Adhering to these guidelines can help your favicon appear in search results.&lt;/p>
&lt;h2 id="creating-a-favicon">Creating a Favicon&lt;/h2>
&lt;p>To create a favicon, use the following site:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://www.favicon-generator.org/" target="_blank" rel="noopener"
>Favicon.ico &amp;amp; App Icon Generator&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>When you visit the site, you&amp;rsquo;ll see a screen like this.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/010-favicon/img-010-001.png"
width="3336"
height="1510"
srcset="https://bossagyu.com/en/blog/010-favicon/img-010-001_hu49ad1e6722066171a92ca460c1768e6d_699110_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/010-favicon/img-010-001_hu49ad1e6722066171a92ca460c1768e6d_699110_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Favicon Generator"
class="gallery-image"
data-flex-grow="220"
data-flex-basis="530px"
>&lt;/p>
&lt;p>Enter the URL of the site you want a favicon for and click on Generate Favicon. Then, on the displayed screen, click the &amp;lsquo;Download the generated favicon&amp;rsquo; link to download the favicon.&lt;/p>
&lt;h2 id="displaying-a-favicon-in-hugo">Displaying a Favicon in Hugo&lt;/h2>
&lt;p>To display a favicon in Hugo, the process varies depending on the theme. For the &lt;a class="link" href="https://github.com/clente/hugo-bearcub/tree/main" target="_blank" rel="noopener"
>bearcub theme&lt;/a>, you can simply set it in the toml like this:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>params&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">favicon&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;images/favicon.ico&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article explained how to create a favicon and display it in Hugo. Favicons are displayed in bookmarks, tabs, and home screens, so it&amp;rsquo;s a good idea to create one for your blog.&lt;/p></description></item><item><title>Introduction to Using Lighthouse</title><link>https://bossagyu.com/en/blog/009-light-house/</link><pubDate>Fri, 22 Dec 2023 23:08:00 +0900</pubDate><guid>https://bossagyu.com/en/blog/009-light-house/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to measure your blog&amp;rsquo;s performance using Lighthouse.&lt;/p>
&lt;h2 id="what-is-lighthouse">What is Lighthouse?&lt;/h2>
&lt;p>Lighthouse is a performance measurement tool for websites provided by Google. It&amp;rsquo;s available as a Google Chrome extension and can be used by installing the plugin.&lt;/p>
&lt;h2 id="installing-lighthouse">Installing Lighthouse&lt;/h2>
&lt;p>Install Lighthouse from the Chrome Web Store.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/009-light-house/img-009-001.png"
width="3090"
height="528"
srcset="https://bossagyu.com/en/blog/009-light-house/img-009-001_huebb24c65f9e0514e53794d84dae1d193_147708_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/009-light-house/img-009-001_huebb24c65f9e0514e53794d84dae1d193_147708_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Chrome Store"
class="gallery-image"
data-flex-grow="585"
data-flex-basis="1404px"
>&lt;/p>
&lt;p>Open the site you want to analyze and click on the Lighthouse icon.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/009-light-house/img-009-002.png"
width="976"
height="184"
srcset="https://bossagyu.com/en/blog/009-light-house/img-009-002_hu79a554b91c1fd5ce9f2d244e8889fd8e_12110_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/009-light-house/img-009-002_hu79a554b91c1fd5ce9f2d244e8889fd8e_12110_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Icon"
class="gallery-image"
data-flex-grow="530"
data-flex-basis="1273px"
>&lt;/p>
&lt;p>Click on Generate report to start the analysis.&lt;br>
This time, I ran it on my &lt;a class="link" href="https://bossagyu.com/blog/001-hugo-netlify-build/" target="_blank" rel="noopener"
>blog page&lt;/a>.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/009-light-house/img-009-003.png"
width="546"
height="1048"
srcset="https://bossagyu.com/en/blog/009-light-house/img-009-003_hu32cc9d1c5498eef6660862acfbb0c7a0_88803_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/009-light-house/img-009-003_hu32cc9d1c5498eef6660862acfbb0c7a0_88803_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Execution Screen"
class="gallery-image"
data-flex-grow="52"
data-flex-basis="125px"
>&lt;/p>
&lt;p>The results are displayed as follows, taking about 1 minute to complete.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/009-light-house/img-009-004.png"
width="1756"
height="906"
srcset="https://bossagyu.com/en/blog/009-light-house/img-009-004_hu5ee3cbd9121e8af4f867f831e4523042_214018_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/009-light-house/img-009-004_hu5ee3cbd9121e8af4f867f831e4523042_214018_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Results"
class="gallery-image"
data-flex-grow="193"
data-flex-basis="465px"
>&lt;/p>
&lt;h2 id="interpreting-the-results">Interpreting the Results&lt;/h2>
&lt;h3 id="performance">Performance&lt;/h3>
&lt;p>Evaluates web performance, like page loading and image display speeds. Clicking the See calculator link takes you to more details.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/009-light-house/img-009-005.png"
width="2236"
height="1222"
srcset="https://bossagyu.com/en/blog/009-light-house/img-009-005_hu09a21b450202618b8da67da25ceb5565_223078_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/009-light-house/img-009-005_hu09a21b450202618b8da67da25ceb5565_223078_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Performance"
class="gallery-image"
data-flex-grow="182"
data-flex-basis="439px"
>&lt;/p>
&lt;h3 id="accessibility">Accessibility&lt;/h3>
&lt;p>Checks whether all users can access content and navigate efficiently within the site. Scrolling down shows areas flagged by Accessibility.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/009-light-house/img-009-006.png"
width="1790"
height="1238"
srcset="https://bossagyu.com/en/blog/009-light-house/img-009-006_hu87f9c989c987a1b95b21e6ceb46b2c76_168048_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/009-light-house/img-009-006_hu87f9c989c987a1b95b21e6ceb46b2c76_168048_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Accessibility"
class="gallery-image"
data-flex-grow="144"
data-flex-basis="347px"
>&lt;/p>
&lt;p>It points out weak color contrast in the code snippets and missing descriptions in links.&lt;/p>
&lt;p>However, the flagged content is not from my writing but depends on the template, so to fix this, it would be necessary to override the Hugo template.&lt;/p>
&lt;h3 id="best-practices">Best Practices&lt;/h3>
&lt;p>Tests the integrity of web pages. You can view the testing items in the results.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/009-light-house/img-009-007.png"
width="962"
height="1070"
srcset="https://bossagyu.com/en/blog/009-light-house/img-009-007_hub9685d9d8ccde516e384b3026e5fd960_91137_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/009-light-house/img-009-007_hub9685d9d8ccde516e384b3026e5fd960_91137_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Best Practices"
class="gallery-image"
data-flex-grow="89"
data-flex-basis="215px"
>&lt;/p>
&lt;h3 id="seo">SEO&lt;/h3>
&lt;p>You can check if the page is optimized for search engine result rankings.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/009-light-house/img-009-008.png"
width="957"
height="1215"
srcset="https://bossagyu.com/en/blog/009-light-house/img-009-008_hu4f2424bc6d0c40fadc50e3b571a98160_118994_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/009-light-house/img-009-008_hu4f2424bc6d0c40fadc50e3b571a98160_118994_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse SEO"
class="gallery-image"
data-flex-grow="78"
data-flex-basis="189px"
>&lt;/p>
&lt;h3 id="progressive-web-app">Progressive Web App&lt;/h3>
&lt;p>Checks if the loading speed of web pages on smartphones is optimized and if it&amp;rsquo;s suitable for PWAs. This wasn&amp;rsquo;t checked in this case.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>The article explained how to measure the performance of a blog using Lighthouse.
Especially for SEO, as it affects visibility in Google search results, it&amp;rsquo;s important to address these issues adequately.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/001-hugo-netlify-build/" >Publishing a Blog with Hugo + Netlify + Github&lt;/a> - Blog initial setup&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/003-google-analytics/" >How to Set Up Google Analytics with Hugo&lt;/a> - Adding analytics&lt;/li>
&lt;/ul></description></item><item><title>How to Schedule Lambda Functions with AWS EventBridge</title><link>https://bossagyu.com/en/blog/008-aws-eventbrdge/</link><pubDate>Thu, 21 Dec 2023 23:03:13 +0900</pubDate><guid>https://bossagyu.com/en/blog/008-aws-eventbrdge/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to use AWS EventBridge to schedule Lambda functions for periodic execution.&lt;/p>
&lt;p>I used this approach to implement scheduled reminder notifications for a LINE Bot.
For example, you can send cleaning reminders every morning at 7 AM without managing any servers.&lt;/p>
&lt;h2 id="what-is-aws-eventbridge">What is AWS EventBridge?&lt;/h2>
&lt;p>AWS EventBridge is a service that facilitates event passing between AWS services.
It has two main use cases:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Use Case&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Scheduled Execution&lt;/td>
&lt;td>Run Lambda periodically using cron or rate expressions&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Event-Driven&lt;/td>
&lt;td>Trigger Lambda on events like S3 file uploads&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>This article focuses on scheduled execution.&lt;/p>
&lt;h2 id="real-world-use-cases">Real-World Use Cases&lt;/h2>
&lt;p>EventBridge + Lambda scheduled execution is useful for:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Reminder notifications&lt;/strong>: Send daily notifications to Slack or LINE at fixed times&lt;/li>
&lt;li>&lt;strong>Batch processing&lt;/strong>: Aggregate data hourly and save to database&lt;/li>
&lt;li>&lt;strong>Health checks&lt;/strong>: Monitor external API availability every 5 minutes&lt;/li>
&lt;li>&lt;strong>Cleanup tasks&lt;/strong>: Delete old logs every night&lt;/li>
&lt;/ul>
&lt;p>I use this for my &lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >Cleaning Reminder Bot&lt;/a> to check notification conditions every hour.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>This guide assumes you have already created a Lambda function.
For instructions on creating Lambda functions, see the &lt;a class="link" href="https://aws.amazon.com/lambda/getting-started/" target="_blank" rel="noopener"
>AWS Lambda Getting Started guide&lt;/a>.&lt;/p>
&lt;h2 id="steps">Steps&lt;/h2>
&lt;h3 id="1-add-a-trigger">1. Add a Trigger&lt;/h3>
&lt;p>Select the Lambda function you want to schedule with EventBridge and click &amp;ldquo;Add trigger&amp;rdquo;.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-001.png"
width="1682"
height="608"
srcset="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-001_hua3391a0a4e37513ed67145a092be2b5f_101439_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-001_hua3391a0a4e37513ed67145a092be2b5f_101439_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Trigger addition screen"
class="gallery-image"
data-flex-grow="276"
data-flex-basis="663px"
>&lt;/p>
&lt;h3 id="2-select-eventbridge">2. Select EventBridge&lt;/h3>
&lt;p>Choose &amp;ldquo;EventBridge (CloudWatch Events)&amp;rdquo; from the trigger options.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-002.png"
width="930"
height="400"
srcset="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-002_hu8fd7668508fa3ea65743ea0e537fb483_58214_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-002_hu8fd7668508fa3ea65743ea0e537fb483_58214_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Selecting EventBridge"
class="gallery-image"
data-flex-grow="232"
data-flex-basis="558px"
>&lt;/p>
&lt;h3 id="3-configure-the-schedule">3. Configure the Schedule&lt;/h3>
&lt;p>After selecting EventBridge, you&amp;rsquo;ll be prompted to create a rule. Configure the schedule as needed.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-003.png"
width="891"
height="903"
srcset="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-003_hub807cbe0354fdfc130d1a185b4a890c6_114907_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-003_hub807cbe0354fdfc130d1a185b4a890c6_114907_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Trigger configuration"
class="gallery-image"
data-flex-grow="98"
data-flex-basis="236px"
>&lt;/p>
&lt;h3 id="4-setup-complete">4. Setup Complete&lt;/h3>
&lt;p>Once configured, EventBridge will appear as a trigger in the Lambda function diagram.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-004.png"
width="1761"
height="976"
srcset="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-004_huc419c6d31f69165530a8b0d8165aa994_183418_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-004_huc419c6d31f69165530a8b0d8165aa994_183418_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Configuration completion screen"
class="gallery-image"
data-flex-grow="180"
data-flex-basis="433px"
>&lt;/p>
&lt;h2 id="cron-expression-syntax">Cron Expression Syntax&lt;/h2>
&lt;p>EventBridge cron expressions consist of 6 fields:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">cron(minute hour day month day-of-week year)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="common-patterns">Common Patterns&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Timing&lt;/th>
&lt;th>Cron Expression&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Daily at 9 AM (UTC)&lt;/td>
&lt;td>&lt;code>cron(0 9 * * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Daily at 9 AM (JST = UTC+9)&lt;/td>
&lt;td>&lt;code>cron(0 0 * * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Every hour at minute 0&lt;/td>
&lt;td>&lt;code>cron(0 * * * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Every 5 minutes&lt;/td>
&lt;td>&lt;code>cron(0/5 * * * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Weekdays only at 9 AM (UTC)&lt;/td>
&lt;td>&lt;code>cron(0 9 ? * MON-FRI *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>First day of each month at midnight (UTC)&lt;/td>
&lt;td>&lt;code>cron(0 0 1 * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="note-timezone-is-utc">Note: Timezone is UTC&lt;/h3>
&lt;p>EventBridge cron expressions use &lt;strong>UTC timezone&lt;/strong>. To schedule in JST (Japan Standard Time), subtract 9 hours.&lt;/p>
&lt;p>Example: Run at 7 AM JST → &lt;code>cron(0 22 * * ? *)&lt;/code> (10 PM UTC the previous day)&lt;/p>
&lt;h2 id="testing-the-setup">Testing the Setup&lt;/h2>
&lt;p>I set up a function to send messages to LINE. The setup now sends notifications every 5 minutes:&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-005.png"
width="1494"
height="256"
srcset="https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-005_hu71f6b6c6b7e02c0c07c0e3b24896f2dd_36361_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/008-aws-eventbrdge/img-008-005_hu71f6b6c6b7e02c0c07c0e3b24896f2dd_36361_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Notification from EventBridge-triggered Lambda function"
class="gallery-image"
data-flex-grow="583"
data-flex-basis="1400px"
>&lt;/p>
&lt;p>For testing, set a short interval (like 5 minutes) to verify it works, then change to an appropriate interval for production.&lt;/p>
&lt;h2 id="cost">Cost&lt;/h2>
&lt;p>The EventBridge + Lambda combination is very cost-effective:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Service&lt;/th>
&lt;th>Free Tier&lt;/th>
&lt;th>Cost After Free Tier&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>EventBridge&lt;/td>
&lt;td>Free&lt;/td>
&lt;td>Schedule rules are free&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Lambda&lt;/td>
&lt;td>1M requests/month free&lt;/td>
&lt;td>$0.20 per 1M requests&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For personal projects like reminder bots, you can run nearly for free.&lt;/p>
&lt;h2 id="common-pitfalls">Common Pitfalls&lt;/h2>
&lt;h3 id="1-timezone-confusion">1. Timezone Confusion&lt;/h3>
&lt;p>As mentioned, EventBridge uses UTC. A common mistake is setting &amp;ldquo;9 AM&amp;rdquo; and finding it runs at 6 PM instead.&lt;/p>
&lt;h3 id="2-initial-execution-timing">2. Initial Execution Timing&lt;/h3>
&lt;p>After setting up a cron expression, you need to wait for the next scheduled time.
To test immediately, use Lambda&amp;rsquo;s built-in test feature.&lt;/p>
&lt;h3 id="3-dont-forget-to-delete">3. Don&amp;rsquo;t Forget to Delete&lt;/h3>
&lt;p>Test EventBridge rules left running will keep triggering Lambda.
Always delete rules you no longer need.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article covered how to schedule Lambda functions with AWS EventBridge.&lt;/p>
&lt;ul>
&lt;li>EventBridge supports both scheduled execution and event-driven triggers&lt;/li>
&lt;li>Cron expressions use UTC timezone - subtract 9 hours for JST&lt;/li>
&lt;li>Running costs are nearly free for personal projects&lt;/li>
&lt;li>Don&amp;rsquo;t forget to delete unused rules&lt;/li>
&lt;/ul>
&lt;p>Serverless scheduled execution is great for reminder bots and batch processing. Give it a try!&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/" >Integrating AWS API Gateway with Lambda&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >How to Use the Cleaning Reminder Bot&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/046-clean-bot-technical/" >Building a Cleaning Reminder Bot - Technical Guide&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Using Google Search Console to Make Your Blog Searchable on Google</title><link>https://bossagyu.com/en/blog/007-google-search-console/</link><pubDate>Mon, 18 Dec 2023 19:10:04 +0900</pubDate><guid>https://bossagyu.com/en/blog/007-google-search-console/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>To appear in Google searches, it&amp;rsquo;s not enough just to apply SEO strategies; your site must first be recognized by Google. This article explains how to make your own custom domain blog appear in Google search results using Google Search Console.&lt;/p>
&lt;h2 id="steps-to-implementation">Steps to Implementation&lt;/h2>
&lt;ol>
&lt;li>Registering with Google Search Console&lt;/li>
&lt;li>Verifying Domain Ownership&lt;/li>
&lt;li>Registering the Sitemap&lt;/li>
&lt;li>Requesting Index Registration&lt;/li>
&lt;li>Summary&lt;/li>
&lt;/ol>
&lt;h2 id="registering-with-google-search-console">Registering with Google Search Console&lt;/h2>
&lt;p>Register on &lt;a class="link" href="https://search.google.com/search-console/welcome" target="_blank" rel="noopener"
>Google Search Console&lt;/a>.&lt;/p>
&lt;p>Choose your domain and enter the URL.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/007-google-search-console/img-007-001.png"
width="1838"
height="1546"
srcset="https://bossagyu.com/en/blog/007-google-search-console/img-007-001_hueec663f85e06a371439303da89c78565_290083_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/007-google-search-console/img-007-001_hueec663f85e06a371439303da89c78565_290083_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="URL input screen"
class="gallery-image"
data-flex-grow="118"
data-flex-basis="285px"
>&lt;/p>
&lt;h2 id="verifying-domain-ownership">Verifying Domain Ownership&lt;/h2>
&lt;p>A screen like the following will appear to verify DNS ownership.&lt;br>
(The TXT record content is blacked out for privacy.)&lt;br>
&lt;img src="https://bossagyu.com/en/blog/007-google-search-console/img-007-002.png"
width="1562"
height="1404"
srcset="https://bossagyu.com/en/blog/007-google-search-console/img-007-002_hu90d036695b5d9438a5ef8f0d8eb9f52d_277880_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/007-google-search-console/img-007-002_hu90d036695b5d9438a5ef8f0d8eb9f52d_277880_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="DNS ownership confirmation screen"
class="gallery-image"
data-flex-grow="111"
data-flex-basis="267px"
>&lt;/p>
&lt;p>You can verify ownership by adding a string specified by Google to your domain&amp;rsquo;s TXT record.
Go to your domain&amp;rsquo;s DNS settings and add a TXT record.&lt;/p>
&lt;p>In my case, I acquired the domain through Netlify, so I went to Netlify&amp;rsquo;s DNS settings.
Navigate to &lt;code>Domains -&amp;gt; Domain Settings -&amp;gt; DNS Records&lt;/code> and add the TXT record.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/007-google-search-console/img-007-003.png"
width="2510"
height="1058"
srcset="https://bossagyu.com/en/blog/007-google-search-console/img-007-003_hu314a39771588211da36db92d8d263e34_237067_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/007-google-search-console/img-007-003_hu314a39771588211da36db92d8d263e34_237067_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Netlify DNS Settings Screen"
class="gallery-image"
data-flex-grow="237"
data-flex-basis="569px"
>&lt;/p>
&lt;p>Copy the content displayed on Google Search Console and paste it into the Value field.&lt;br>
(The Value part is blacked out for privacy.)&lt;br>
&lt;img src="https://bossagyu.com/en/blog/007-google-search-console/img-007-004.png"
width="1174"
height="1128"
srcset="https://bossagyu.com/en/blog/007-google-search-console/img-007-004_hu1e1863a2c4e003478336f70e9eafc56f_103599_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/007-google-search-console/img-007-004_hu1e1863a2c4e003478336f70e9eafc56f_103599_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Example of adding DNS"
class="gallery-image"
data-flex-grow="104"
data-flex-basis="249px"
>&lt;/p>
&lt;p>Wait for DNS updates, which can take a few hours depending on the provider.&lt;/p>
&lt;p>You can check DNS propagation from the command line.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">dig -t txt bossagyu.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Afterward, press the verify ownership button on Google Search Console.&lt;br>
This completes the verification process, and your domain will be registered with Google Search Console.&lt;/p>
&lt;h2 id="registering-the-sitemap">Registering the Sitemap&lt;/h2>
&lt;p>Registering a sitemap informs Google about the structure of your site, facilitating the crawling process.
For blogs created with Hugo, the sitemap is available at &lt;code>/sitemap.xml&lt;/code>, which you should register.&lt;/p>
&lt;p>From the left menu of Google Search Console, select &amp;lsquo;Sitemaps&amp;rsquo; and add your sitemap.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/007-google-search-console/img-007-005.png"
width="1401"
height="649"
srcset="https://bossagyu.com/en/blog/007-google-search-console/img-007-005_huab186b2aac0819b0a23c39035e1f9b13_109847_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/007-google-search-console/img-007-005_huab186b2aac0819b0a23c39035e1f9b13_109847_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Sitemap Registration Screen"
class="gallery-image"
data-flex-grow="215"
data-flex-basis="518px"
>&lt;/p>
&lt;h2 id="requesting-index-registration">Requesting Index Registration&lt;/h2>
&lt;p>Even if your site is registered in the sitemap, it can take time for Google to crawl and index it.
In my case, I requested index registration after waiting several days without being indexed.&lt;/p>
&lt;p>Search for the URL you want to register in Google Search Console, and click on &amp;lsquo;Request Indexing&amp;rsquo; found on the right side of the search result.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/007-google-search-console/img-007-006.png"
width="2900"
height="868"
srcset="https://bossagyu.com/en/blog/007-google-search-console/img-007-006_hu89514150a49eb25441f9d3432897d97a_212853_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/007-google-search-console/img-007-006_hu89514150a49eb25441f9d3432897d97a_212853_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Request index registration"
class="gallery-image"
data-flex-grow="334"
data-flex-basis="801px"
>&lt;/p>
&lt;p>This requests index registration.
It took a few hours for the index to be registered after clicking.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article explained how to make your custom domain blog searchable using Google Search Console.&lt;br>
It&amp;rsquo;s a waste not to have your blog appear in Google searches after all the effort of creating it, so give it a try.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/001-hugo-netlify-build/" >Publishing a Blog with Hugo + Netlify + Github&lt;/a> - Blog initial setup&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/003-google-analytics/" >How to Set Up Google Analytics with Hugo&lt;/a> - Adding analytics&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/009-light-house/" >Introduction to Using Lighthouse&lt;/a> - Performance measurement&lt;/li>
&lt;/ul></description></item><item><title>Efficient Lambda Development with AWS Toolkit in IntelliJ</title><link>https://bossagyu.com/en/blog/006-intellij-lamda-setup/</link><pubDate>Tue, 12 Dec 2023 22:40:05 +0900</pubDate><guid>https://bossagyu.com/en/blog/006-intellij-lamda-setup/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to efficiently develop Lambda functions using the AWS Toolkit in IntelliJ.&lt;/p>
&lt;h2 id="steps-to-implementation">Steps to Implementation&lt;/h2>
&lt;ol>
&lt;li>Preliminary Preparation&lt;/li>
&lt;li>Installing AWS Toolkit&lt;/li>
&lt;li>Configuring AWS Toolkit&lt;/li>
&lt;li>Developing Lambda&lt;/li>
&lt;li>Executing Lambda Locally&lt;/li>
&lt;li>Summary&lt;/li>
&lt;/ol>
&lt;h2 id="preliminary-preparation">Preliminary Preparation&lt;/h2>
&lt;h3 id="installing-docker">Installing Docker&lt;/h3>
&lt;p>AWS Toolkit in IntelliJ uses Docker to run Lambda.&lt;br>
Prior to proceeding, please install Docker by referring to &lt;a class="link" href="https://docs.docker.jp/docker-for-mac/install.html" target="_blank" rel="noopener"
>these instructions&lt;/a>.&lt;/p>
&lt;h3 id="installing-aws-cli">Installing AWS CLI&lt;/h3>
&lt;p>Install AWS CLI (SAM).&lt;br>
For installation, refer to &lt;a class="link" href="https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-mac.html" target="_blank" rel="noopener"
>these instructions&lt;/a>.&lt;/p>
&lt;p>In IntelliJ, set the path for SAM CLI executable under &lt;code>File -&amp;gt; Settings -&amp;gt; Tools -&amp;gt; AWS Toolkit&lt;/code>.&lt;br>
In my case, as I installed it through brew, I set the following path.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-005.png"
width="976"
height="707"
srcset="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-005_hu675386622417230d840d91c8ebb2bb8f_99529_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-005_hu675386622417230d840d91c8ebb2bb8f_99529_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Intellij configuration"
class="gallery-image"
data-flex-grow="138"
data-flex-basis="331px"
>&lt;/p>
&lt;h2 id="installing-aws-toolkit">Installing AWS Toolkit&lt;/h2>
&lt;p>Install the AWS Toolkit via IntelliJ plugins.
Refer to &lt;a class="link" href="https://www.jetbrains.com/help/idea/managing-plugins.html#install_plugin" target="_blank" rel="noopener"
>this guide&lt;/a> for plugin installation.&lt;/p>
&lt;h2 id="configuring-aws-toolkit">Configuring AWS Toolkit&lt;/h2>
&lt;p>To use AWS Toolkit, you need to set up your AWS credentials.&lt;/p>
&lt;p>Set up AWS credentials through AWS Explorer.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-001.png"
width="2196"
height="1099"
srcset="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-001_huf4116ed306cba8df785a2a60b5b23b2e_297569_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-001_huf4116ed306cba8df785a2a60b5b23b2e_297569_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWS Authentication"
class="gallery-image"
data-flex-grow="199"
data-flex-basis="479px"
>&lt;/p>
&lt;p>Obtain and configure your Access Key ID and Secret Access Key from the AWS console.
Once configured, AWS resources should appear in AWS Explorer.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-002.png"
width="400"
height="517"
srcset="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-002_huef1a4e77eb43fc9eb01b7ee491b04729_36733_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-002_huef1a4e77eb43fc9eb01b7ee491b04729_36733_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWS resource addition confirmation"
class="gallery-image"
data-flex-grow="77"
data-flex-basis="185px"
>&lt;/p>
&lt;p>Note: In this image, the region is set to &lt;code>us-east-1&lt;/code>. Please adjust according to the region where you intend to create your Lambda.&lt;/p>
&lt;h2 id="developing-lambda">Developing Lambda&lt;/h2>
&lt;p>Create a code snippet like the following.&lt;/p>
&lt;p>lambda-sample.py&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">lambda_handler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Hello World&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Hello World!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Create a Lambda through AWS Explorer.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-003.png"
width="382"
height="452"
srcset="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-003_hu43e53933fae6825db1f62c0ff344925c_42804_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-003_hu43e53933fae6825db1f62c0ff344925c_42804_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lambda function creation screen"
class="gallery-image"
data-flex-grow="84"
data-flex-basis="202px"
>&lt;/p>
&lt;p>Select Create Lambda Function and input the necessary values.&lt;br>
For the Handler, enter &lt;code>&amp;lt;filename&amp;gt;.&amp;lt;function name&amp;gt;&lt;/code> from your code snippet.&lt;br>
&lt;img src="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-004.png"
width="769"
height="626"
srcset="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-004_hub0ca7c127cd2ded12c0d6c6eb7b0efd4_80439_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-004_hub0ca7c127cd2ded12c0d6c6eb7b0efd4_80439_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lambda function setup"
class="gallery-image"
data-flex-grow="122"
data-flex-basis="294px"
>&lt;/p>
&lt;p>This completes the creation of your Lambda.&lt;/p>
&lt;h2 id="executing-lambda-locally">Executing Lambda Locally&lt;/h2>
&lt;p>The Toolkit also allows you to execute Lambda locally.
&lt;img src="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-006.png"
width="544"
height="181"
srcset="https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-006_hue5ae0f1238e4626d26a5e0ef51d83c5a_29793_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/006-intellij-lamda-setup/img-006-006_hue5ae0f1238e4626d26a5e0ef51d83c5a_29793_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lambda function execution screen"
class="gallery-image"
data-flex-grow="300"
data-flex-basis="721px"
>&lt;/p>
&lt;p>Selecting Run will execute the Lambda locally.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article provided a guide on efficiently developing Lambda using AWS Toolkit in IntelliJ.
Developing in IntelliJ and executing locally can significantly improve development efficiency.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/014-aws-apigateway-lambda/" >Integrating AWS API Gateway with Lambda&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/046-clean-bot-technical/" >Building a Cleaning Reminder Bot with AWS Lambda + LINE&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use GitHub Copilot in IntelliJ</title><link>https://bossagyu.com/en/blog/005-github-copilot/</link><pubDate>Mon, 11 Dec 2023 22:45:40 +0900</pubDate><guid>https://bossagyu.com/en/blog/005-github-copilot/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article explains how to use GitHub Copilot in IntelliJ.
Additionally, a cheat sheet of shortcuts is provided.&lt;/p>
&lt;h2 id="steps-to-implementation">Steps to Implementation&lt;/h2>
&lt;ol>
&lt;li>Register for GitHub Copilot&lt;/li>
&lt;li>Configure IntelliJ&lt;/li>
&lt;li>Use GitHub Copilot&lt;/li>
&lt;li>Summary&lt;/li>
&lt;/ol>
&lt;h2 id="registering-for-github-copilot">Registering for GitHub Copilot&lt;/h2>
&lt;p>Register for GitHub Copilot through the &lt;a class="link" href="https://copilot.github.com/" target="_blank" rel="noopener"
>GitHub Copilot&lt;/a> link.&lt;/p>
&lt;h2 id="configuring-intellij">Configuring IntelliJ&lt;/h2>
&lt;p>Install the GitHub Copilot plugin from IntelliJ plugins.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/005-github-copilot/img-005-001.png"
width="972"
height="237"
srcset="https://bossagyu.com/en/blog/005-github-copilot/img-005-001_huf1c35ae508766e7174ebde609be4cd7c_63136_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/005-github-copilot/img-005-001_huf1c35ae508766e7174ebde609be4cd7c_63136_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="GitHub Copilot plugin"
class="gallery-image"
data-flex-grow="410"
data-flex-basis="984px"
>&lt;/p>
&lt;p>Once installed, restart IntelliJ.&lt;/p>
&lt;h2 id="using-github-copilot">Using GitHub Copilot&lt;/h2>
&lt;p>When you write code in IntelliJ, GitHub Copilot will assist with code completion.&lt;/p>
&lt;p>Here is a list of shortcuts for Mac:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Shortcut&lt;/th>
&lt;th>Function&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>tab&lt;/code>&lt;/td>
&lt;td>Complete the code&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Option&lt;/code> + &lt;code>]&lt;/code>&lt;/td>
&lt;td>Show the next completion suggestion&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Option&lt;/code> + &lt;code>[&lt;/code>&lt;/td>
&lt;td>Show the previous completion suggestion&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Command&lt;/code> + &lt;code>→&lt;/code>&lt;/td>
&lt;td>Accept only the next word of the suggestion&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This article explained how to use GitHub Copilot in IntelliJ.
It&amp;rsquo;s worth noting that this article was written using GitHub Copilot, and it significantly assists with blog creation in Markdown, so those interested should give it a try.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/017-vscode-copilot/" >Complete Guide to Using GitHub Copilot in VSCode&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/042-github-copilot/" >How to Use GitHub Copilot More Effectively with Chat Tools&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Setting Up a Local Environment Using Pyenv and venv</title><link>https://bossagyu.com/en/blog/004-python-setup/</link><pubDate>Sun, 10 Dec 2023 23:19:33 +0900</pubDate><guid>https://bossagyu.com/en/blog/004-python-setup/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article documents how to set up a Python environment for development on a Mac local environment.&lt;br>
In this case, we will use two systems to manage different versions of Python and virtual environments:&lt;/p>
&lt;ul>
&lt;li>pyenv
&lt;ul>
&lt;li>Used to handle multiple versions of Python.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>venv
&lt;ul>
&lt;li>Used to separate environments for each project.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>For explanations on the differences and the necessity of each, this &lt;a class="link" href="https://jimaru.blog/programming/python/venv_pyenv_choice/" target="_blank" rel="noopener"
>article&lt;/a> is a helpful reference.&lt;/p>
&lt;h2 id="installing-python">Installing Python&lt;/h2>
&lt;p>First, install Pyenv on your local environment to use a specific version of Python.&lt;/p>
&lt;p>Install pyenv.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">brew install pyenv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Check the installed version of pyenv.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv --version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv 2.3.35
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Add settings to zsh.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PYENV_ROOT=&amp;#34;$HOME/.pyenv&amp;#34;&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PATH=&amp;#34;$PYENV_ROOT/bin:$PATH&amp;#34;&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> -e &lt;span class="s1">&amp;#39;if command -v pyenv 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then\n eval &amp;#34;$(pyenv init -)&amp;#34;\nfi&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Reload &lt;code>.zshrc&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> ~/.zshrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Display a list of installable Python versions.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv install --list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install the specified version.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv install 3.11.7
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Use the specified Python version in your project folder.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> &amp;lt;created project folder&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv &lt;span class="nb">local&lt;/span> 3.11.7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv versions
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If global, it will be applied to the entire system.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv global 3.11.7
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Check the version of Python being executed.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python -V
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="creating-a-virtual-environment-with-venv">Creating a Virtual Environment with venv&lt;/h2>
&lt;p>Create a virtual environment in the project directory.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># python -m venv &amp;lt;virtual environment name&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python -m venv venv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Activate the virtual environment.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> venv/bin/activate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>To deactivate, execute the following command.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">deactivate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="how-to-update-a-venv">How to Update a venv&lt;/h2>
&lt;p>If you need to refresh dependencies, activate the virtual environment and run the following. When using &lt;code>requirements.txt&lt;/code>, update the file first and then reinstall.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> venv/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install --upgrade pip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install -U -r requirements.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>When upgrading Python to a new minor version, install the new version with &lt;code>pyenv&lt;/code>, recreate the virtual environment, and reinstall dependencies to avoid mismatches.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv install 3.12.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv &lt;span class="nb">local&lt;/span> 3.12.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -rf venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python -m venv venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> venv/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install -U -r requirements.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="how-to-delete-a-venv">How to Delete a venv&lt;/h2>
&lt;p>When the virtual environment is no longer needed, delete the directory. Confirm you are in the correct working directory before running the command.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">rm -rf venv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This completes the setup of the local environment.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/032-python-uv/" >Setting Up a Python Development Environment on Mac with UV&lt;/a> (as a modern alternative)&lt;/li>
&lt;/ul></description></item><item><title>How to Set Up Google Analytics with Hugo</title><link>https://bossagyu.com/en/blog/003-google-analytics/</link><pubDate>Sat, 09 Dec 2023 18:09:42 +0900</pubDate><guid>https://bossagyu.com/en/blog/003-google-analytics/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This article briefly explains how to set up Google Analytics with Hugo.&lt;/p>
&lt;h2 id="steps-to-implementation">Steps to Implementation&lt;/h2>
&lt;ol>
&lt;li>Register with Google Analytics&lt;/li>
&lt;li>Obtain the Tracking ID&lt;/li>
&lt;li>Add the Tracking ID to Hugo&amp;rsquo;s configuration&lt;/li>
&lt;/ol>
&lt;h2 id="registering-with-google-analytics">Registering with Google Analytics&lt;/h2>
&lt;p>Follow the instructions on &lt;a class="link" href="https://support.google.com/analytics/answer/9304153?hl=ja" target="_blank" rel="noopener"
>Setting up a new website or app with GA4&lt;/a> to register.&lt;br>
When you add a data stream, you will get a Tracking ID, so make a note of it.
&lt;img src="https://bossagyu.com/en/blog/003-google-analytics/img-003-001.png"
width="1262"
height="387"
srcset="https://bossagyu.com/en/blog/003-google-analytics/img-003-001_hud6bb49f0ab37d8b4799433827135bf4a_61149_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/003-google-analytics/img-003-001_hud6bb49f0ab37d8b4799433827135bf4a_61149_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Add Data Stream Screen"
class="gallery-image"
data-flex-grow="326"
data-flex-basis="782px"
>
※ The Tracking ID may be displayed as &amp;ldquo;Measurement ID&amp;rdquo; due to translation.&lt;/p>
&lt;h2 id="adding-tracking-id-to-hugos-configuration">Adding Tracking ID to Hugo&amp;rsquo;s Configuration&lt;/h2>
&lt;h3 id="add-settings-in-toml">Add settings in toml&lt;/h3>
&lt;p>Add &lt;code>googleAnalytics = Tracking ID&lt;/code> to your config.toml.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">baseURL&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;https://bossagyu.com&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">languageCode&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;ja-jp&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">title&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;Bossagyu Blog&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">theme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;hugo-bearcub&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">googleAnalytics&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;G-1234ABCDEF&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># ↑ Add this line, replace the Tracking ID with your own.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="embedding-the-tracking-code">Embedding the Tracking Code&lt;/h3>
&lt;p>Some templates might read settings from the toml file, but the &lt;a class="link" href="https://github.com/clente/hugo-bearcub/tree/main" target="_blank" rel="noopener"
>bearcub&lt;/a> template I use does not support this, so I added the tracking code to the header myself.&lt;/p>
&lt;p>For the code snippet, I referred to &lt;a class="link" href="https://github.com/clente/hugo-bearcub/tree/main" target="_blank" rel="noopener"
>Makumaku Hugo Notes&lt;/a>.&lt;/p>
&lt;p>Create &lt;code>layouts/partials/analytics.html&lt;/code> to load the tracking code.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ if not .Site.IsServer }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ with .Site.GoogleAnalytics }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Google tag (gtag.js) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">async&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://www.googletagmanager.com/gtag/js?id={{ . }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dataLayer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dataLayer&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">gtag&lt;/span>&lt;span class="p">(){&lt;/span>&lt;span class="nx">dataLayer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arguments&lt;/span>&lt;span class="p">);}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">gtag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;js&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">gtag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;config&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;{{ . }}&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Make the page header load &lt;code>analytics.html&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Copy the content of the template to override it&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cp themes/hugo-bearcub/layouts/_default/baseof.html layouts/_default/baseof.html
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">vim layouts/_default/baseof.html
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Add &lt;code>{{- partial &amp;quot;analytics&amp;quot; . -}}&lt;/code> to baseof.html.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!DOCTYPE html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">html&lt;/span> &lt;span class="na">lang&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ with .Site.LanguageCode }}{{ . }}{{ else }}en-US{{ end }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{- partial &amp;#34;analytics&amp;#34; . -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">http-equiv&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;X-Clacks-Overhead&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;GNU Terry Pratchett&amp;#34;&lt;/span> &lt;span class="err">/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After adding the above source code and rebuilding, data will be sent to Google Analytics.&lt;/p>
&lt;h2 id="tips">Tips&lt;/h2>
&lt;p>If it seems like data is not being transmitted to Google Analytics despite these steps, it&amp;rsquo;s possible that the tags have not been added correctly.&lt;br>
To troubleshoot, first check if the tracking is included in the HTML by using Google Developer Tools.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/001-hugo-netlify-build/" >Publishing a Blog with Hugo + Netlify + Github&lt;/a> - Blog initial setup&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/007-google-search-console/" >Using Google Search Console to Make Your Blog Searchable on Google&lt;/a> - SEO setup&lt;/li>
&lt;/ul></description></item><item><title>Registering and Using the LINE Messaging API</title><link>https://bossagyu.com/en/blog/002-line-messaging-api/</link><pubDate>Thu, 07 Dec 2023 09:37:00 +0900</pubDate><guid>https://bossagyu.com/en/blog/002-line-messaging-api/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>I thought of creating an application using LINE&amp;rsquo;s Bot, so first, I will make the Bot usable.&lt;br>
This page introduces how to register for the LINE Messaging API and how to send messages from the command line using curl.&lt;/p>
&lt;h2 id="using-the-messaging-api">Using the Messaging API&lt;/h2>
&lt;p>Log in to &lt;a class="link" href="https://developers.line.biz/console/" target="_blank" rel="noopener"
>LINE Developers&lt;/a> and create a provider.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/002-line-messaging-api/img-002-001.png"
width="1542"
height="947"
srcset="https://bossagyu.com/en/blog/002-line-messaging-api/img-002-001_hu0f14840654aed48d16eb2fc9422f6fa9_170581_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/002-line-messaging-api/img-002-001_hu0f14840654aed48d16eb2fc9422f6fa9_170581_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Provider Creation Screen"
class="gallery-image"
data-flex-grow="162"
data-flex-basis="390px"
>&lt;/p>
&lt;p>A provider is (&lt;a class="link" href="https://developers.line.biz/ja/docs/line-developers-console/overview/#provider" target="_blank" rel="noopener"
>Explanation&lt;/a>)&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">On the LINE Developers site, a service provider refers to individuals, companies, or organizations that provide services and obtain user information (service proprietor in LINE Mini Apps).
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>So, you can enter any string you like.&lt;/p>
&lt;p>Then, create a new channel.
&lt;img src="https://bossagyu.com/en/blog/002-line-messaging-api/img-002-002.png"
width="1393"
height="1461"
srcset="https://bossagyu.com/en/blog/002-line-messaging-api/img-002-002_hu35721a5501bd4d5408347db79d1d688a_163848_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/002-line-messaging-api/img-002-002_hu35721a5501bd4d5408347db79d1d688a_163848_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Channel creation screen"
class="gallery-image"
data-flex-grow="95"
data-flex-basis="228px"
>&lt;/p>
&lt;p>Clicking the create button will establish a new channel.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/002-line-messaging-api/img-002-003.png"
width="848"
height="267"
srcset="https://bossagyu.com/en/blog/002-line-messaging-api/img-002-003_hudf15c356e8d7f64c7636ad582293d1a9_33429_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/002-line-messaging-api/img-002-003_hudf15c356e8d7f64c7636ad582293d1a9_33429_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Channel creation results"
class="gallery-image"
data-flex-grow="317"
data-flex-basis="762px"
>&lt;/p>
&lt;h1 id="posting-from-the-command-line">Posting from the Command Line&lt;/h1>
&lt;p>Add friends by reading the QR code in the Messaging API settings.&lt;/p>
&lt;p>Obtain the &amp;lsquo;Channel Access Token (Long-lived)&amp;rsquo; from the Messaging API settings.
Get &amp;lsquo;Your User ID&amp;rsquo; from the channel basic settings.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TOKEN&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;Channel Access Token (Long-lived)&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;Your User ID&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">UUID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>uuidgen &lt;span class="p">|&lt;/span> tr &lt;span class="s2">&amp;#34;[:upper:]&amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;[:lower:]&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -v -X POST https://api.line.me/v2/bot/message/push &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>-H &lt;span class="s1">&amp;#39;Content-Type: application/json&amp;#39;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>-H &lt;span class="s2">&amp;#34;Authorization: Bearer &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">TOKEN&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>-H &lt;span class="s2">&amp;#34;X-Line-Retry-Key: &amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>-d &lt;span class="s2">&amp;#34;{
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;to\&amp;#34;: \&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">ID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">\&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;messages\&amp;#34;:[
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;type\&amp;#34;:\&amp;#34;text\&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;text\&amp;#34;:\&amp;#34;Hello, world1\&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> ]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If a response is returned and you see a post from the Bot in your LINE chat, it&amp;rsquo;s a success!&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/045-clean-bot/" >Cleaning Reminder LINE Bot&lt;/a> (a practical LINE Bot project)&lt;/li>
&lt;/ul></description></item><item><title>Publishing a Blog with Hugo + Netlify + Github</title><link>https://bossagyu.com/en/blog/001-hugo-netlify-build/</link><pubDate>Sat, 02 Dec 2023 00:59:37 +0900</pubDate><guid>https://bossagyu.com/en/blog/001-hugo-netlify-build/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This document describes the steps to create a site with Hugo, manage it with Github, and build it with Netlify from scratch.
With this method, you can easily publish by just pushing your Markdown-written blog to Github.&lt;/p>
&lt;p>This guide also covers how to set up a favicon.&lt;/p>
&lt;h2 id="process">Process&lt;/h2>
&lt;ol>
&lt;li>Generate a site with Hugo&lt;/li>
&lt;li>Push to Github&lt;/li>
&lt;li>Deploy with Netlify&lt;/li>
&lt;li>Set up Favicon&lt;/li>
&lt;/ol>
&lt;h2 id="generating-a-static-site-with-hugo">Generating a Static Site with Hugo&lt;/h2>
&lt;p>First, install Hugo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">brew install hugo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Create a template for the blog.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">hugo new site my-blog
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Add a theme suitable for the blog as a submodule.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my-blog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Add the theme as a submodule from Github&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Apply the theme by adding it to hugo.toml.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;theme = &amp;#39;ananke&amp;#39;&amp;#34;&lt;/span> &amp;gt;&amp;gt; config.toml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Start the server.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">hugo server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Access the URL like &lt;code>http://localhost:51517/&lt;/code> shown in the startup log &lt;code>Web Server is available at http://localhost:51517/ (bind address 127.0.0.1)&lt;/code> to view the locally launched static site.&lt;/p>
&lt;h3 id="tips">Tips&lt;/h3>
&lt;ul>
&lt;li>If you want to change the Hugo theme, please choose your favorite one from &lt;a class="link" href="https://themes.gohugo.io/" target="_blank" rel="noopener"
>Hugo Themes&lt;/a>.
&lt;ul>
&lt;li>It&amp;rsquo;s recommended to run through until you build with Netlify first, as this can be changed later.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>The way to write Toml files is described in &lt;a class="link" href="https://gohugo.io/getting-started/configuration/" target="_blank" rel="noopener"
>Configure Hugo&lt;/a>.&lt;/li>
&lt;/ul>
&lt;br>
&lt;h2 id="push-to-github">Push to Github&lt;/h2>
&lt;p>Create a repository on &lt;a class="link" href="https://github.com/" target="_blank" rel="noopener"
>Github&lt;/a>.&lt;br>
After creation, execute the following commands to push your site.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my-blog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> .hugo_build.lock &amp;gt;&amp;gt; .gitignore
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git add .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git commit -m &lt;span class="s2">&amp;#34;first commit&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git branch -M main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Replace &amp;lt;user name&amp;gt; with your own username.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This is an example of creating a repository called my-blog.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git remote add origin git@github.com:&amp;lt;user name&amp;gt;/my-blog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push -u origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Once the push is complete, the source code becomes viewable on the Github UI.&lt;/p>
&lt;h2 id="deploy-with-netlify">Deploy with Netlify&lt;/h2>
&lt;p>Access &lt;a class="link" href="https://www.netlify.com/" target="_blank" rel="noopener"
>Netlify&lt;/a> and perform deployment.&lt;br>
There are instructions on &lt;a class="link" href="https://gohugo.io/hosting-and-deployment/hosting-on-netlify/" target="_blank" rel="noopener"
>Hugo&amp;rsquo;s official website&lt;/a>, so refer to them for integration.&lt;/p>
&lt;p>Follow the instructions to complete the deployment, and the result of the Deploy will be shown as &lt;code>published&lt;/code>.&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/en/blog/001-hugo-netlify-build/img-001-001.png"
width="1259"
height="781"
srcset="https://bossagyu.com/en/blog/001-hugo-netlify-build/img-001-001_hu712c6624ab1de9c2a2e527b803a3005e_132140_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/001-hugo-netlify-build/img-001-001_hu712c6624ab1de9c2a2e527b803a3005e_132140_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Deployment execution result screen"
class="gallery-image"
data-flex-grow="161"
data-flex-basis="386px"
>&lt;/p>
&lt;p>Click on the URL displayed on the site to access the deployed site.
&lt;img src="https://bossagyu.com/en/blog/001-hugo-netlify-build/img-001-002.png"
width="844"
height="274"
srcset="https://bossagyu.com/en/blog/001-hugo-netlify-build/img-001-002_hu73c75e92b3844c61fd78de6d6904914d_56626_480x0_resize_box_3.png 480w, https://bossagyu.com/en/blog/001-hugo-netlify-build/img-001-002_hu73c75e92b3844c61fd78de6d6904914d_56626_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Link display screen"
class="gallery-image"
data-flex-grow="308"
data-flex-basis="739px"
>&lt;/p>
&lt;p>This completes the deployment process.
After this, any changes made and pushed to main will automatically trigger deployment, updating the site content.&lt;/p>
&lt;h2 id="setting-up-favicon">Setting Up Favicon&lt;/h2>
&lt;p>A favicon is an icon displayed in bookmarks, browser tabs, and home screens.
Google has published &lt;a class="link" href="https://developers.google.com/search/docs/appearance/favicon-in-search" target="_blank" rel="noopener"
>favicon guidelines&lt;/a> for appearing in search results.
Following these guidelines will help your favicon appear in search results.&lt;/p>
&lt;h3 id="creating-a-favicon">Creating a Favicon&lt;/h3>
&lt;p>To create a favicon, use the following website:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://www.favicon-generator.org/" target="_blank" rel="noopener"
>Favicon.ico &amp;amp; App Icon Generator&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Access the site, upload the image you want to use as a favicon, and click &amp;ldquo;Generate Favicon&amp;rdquo;.
Then click &amp;ldquo;Download the generated favicon&amp;rdquo; to download your favicon.&lt;/p>
&lt;h3 id="displaying-favicon-in-hugo">Displaying Favicon in Hugo&lt;/h3>
&lt;p>To display a favicon in Hugo, the method varies by theme, but most themes allow you to simply add the following to your toml configuration:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">favicon&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;images/favicon.ico&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Place the downloaded favicon.ico in the &lt;code>static/images/&lt;/code> directory and add the above configuration.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>This guide covered how to publish a blog with Hugo + Netlify + Github.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Hugo&lt;/strong>: Generate static site&lt;/li>
&lt;li>&lt;strong>Github&lt;/strong>: Manage source code&lt;/li>
&lt;li>&lt;strong>Netlify&lt;/strong>: Automatic deployment&lt;/li>
&lt;li>&lt;strong>Favicon&lt;/strong>: Branding configuration&lt;/li>
&lt;/ol>
&lt;p>With this setup, you just write articles in Markdown and push to Github for automatic site updates.&lt;/p>
&lt;h2 id="related-articles">Related Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/003-google-analytics/" >How to Set Up Google Analytics with Hugo&lt;/a> - Adding analytics&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/007-google-search-console/" >How to Add Your Blog to Google Search Using Google Search Console&lt;/a> - SEO setup&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/009-light-house/" >Introduction to Using Lighthouse&lt;/a> - Performance measurement&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/011-hugo-multilingul-support/" >Using ChatGPT to Make a Hugo Blog Multilingual&lt;/a> - Multilingual support&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/en/blog/012-social-card/" >Setting Up Twitter Social Cards&lt;/a> - Social media optimization&lt;/li>
&lt;/ul></description></item></channel></rss>