<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Shrestha Pandey</title>
    <description>The latest articles on Forem by Shrestha Pandey (@shresthapandey).</description>
    <link>https://forem.com/shresthapandey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3775845%2Fa627b42c-6d80-4c14-ba70-55b0c2cbcc08.jpg</url>
      <title>Forem: Shrestha Pandey</title>
      <link>https://forem.com/shresthapandey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shresthapandey"/>
    <language>en</language>
    <item>
      <title>Why Reasoning Models Changed Everything</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Fri, 10 Apr 2026 14:38:33 +0000</pubDate>
      <link>https://forem.com/shresthapandey/why-reasoning-models-changed-everything-5e1n</link>
      <guid>https://forem.com/shresthapandey/why-reasoning-models-changed-everything-5e1n</guid>
      <description>&lt;p&gt;&lt;em&gt;For years, making language models smarter meant making them bigger. Then someone asked a different question: what if, instead of training more, you let the model think longer?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In September 2024, OpenAI released o1. In January 2025, DeepSeek released R1. These two models together, invalidated the assumption that was governing the entire field since 2020,  that the path to better AI runs through bigger training runs.&lt;/p&gt;

&lt;p&gt;This assumption was backed by the Kaplan et al. scaling laws paper, which showed the language model performance follows a reliable power law with training compute. If you provide more parameters, more data, more GPU-hours, you get a more capable model. The field organized itself around this insight. Every major lab poured billions into pre-training. &lt;/p&gt;

&lt;p&gt;o1 and R1 showed that there’s a second dimension to scale that the field had largely ignored: compute at inference time. And it turns out that for tasks requiring multi-step reasoning, this second dimension can be just as powerful as the first, and far cheaper to exploit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What chain-of-thought actually is
&lt;/h2&gt;

&lt;p&gt;Chain-of-thought prompting has been around since 2022, when Wei et al. at Google Brain showed that simply asking a language model to “think step by step” before answering dramatically improved its performance on math and logic tasks. This was a really surprising result. The model wasn’t being retrained, it was just prompted differently. The extra tokens the model generated while reasoning served as scratchpad that improved its final answer.&lt;/p&gt;

&lt;p&gt;Transformers generate one token at a time, and each token is conditioned on all previous tokens. When a model solves a math problem by writing out steps, those steps become part of the context that informs the final answer. The model is using its own output as working memory. Without chain-of-thought, it has to compress all that computation into a single forward pass.&lt;/p&gt;

&lt;p&gt;Nobody had figured out how to train a model to do this reliably, until o1. Prompting a model to “think step by step” helps, but the quality of reasoning is inconsistent. You might get careful and structured reasoning sometimes, but sometimes you get verbose filler that doesn’t really help. The model doesn’t know when to think hard and when not to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reinforcement learning connection
&lt;/h2&gt;

&lt;p&gt;OpenAI’s o1 system card describes training the model with reinforcement learning to produce a chain of thought before answering. The core thing is that RL can teach the model how to think specifically, to develop reasoning strategies that lead to correct answers on verifiable tasks like mathematics and code.&lt;/p&gt;

&lt;p&gt;According to OpenAI’s published description, o1 learns through RL to recognize and correct its mistakes in the middle of reasoning, break hard problems into simpler subproblems, and abandon approaches that aren’t working. These behaviors like self-correction, decomposition, backtracking, emerge from the training signal.&lt;/p&gt;

&lt;p&gt;The result is a model where more thinking time produces better answers. On the AIME 2024 benchmark (American Invitational Mathematics Examination), o1 scored in the 74th percentile of human test-takers. GPT-4o, using the same benchmark, scored around 9%. That gap is from the model being trained to use its inference compute more productively.&lt;/p&gt;

&lt;h2&gt;
  
  
  DeepSeek R1: showing the mechanism
&lt;/h2&gt;

&lt;p&gt;DeepSeek R1 published in January 2025 was an extremely important advancement because it achieved on benchmark scores the same level of performance as o1; however, this is the easy part. The significance of DeepSeek was that it provided an in-depth description of the training recipe allowing other people in the field the ability to study and replicate it.&lt;/p&gt;

&lt;p&gt;They started with DeepSeek-V3-Base, a 671B parameter pretrained model built on a Mixture-of-Experts architecture. Their first experiment called DeepSeek-R1-Zero, applied reinforcement learning directly to this base model without supervised fine-tuning beforehand. The reward signal was such that, the model gets rewarded for producing a correct final answer, and for formatting its output with explicit reasoning inside &lt;code&gt;&amp;lt;think&amp;gt;&lt;/code&gt; tags. &lt;/p&gt;

&lt;p&gt;No direct human-labeled examples were used to provide the basis for generating a reward signal for successful attempts. Similarly, no preference-based human reward model was utilized for training and reward purposes for deep learning via reinforcement learning. Simply put, their reinforcement learning experiment used the following evaluation criteria: (1) Did you provide the correct final answer?&lt;/p&gt;

&lt;p&gt;The results of R1-Zero were remarkable and, to be honest, a little unsettling. The model's average pass@1 score on AIME 2024 increased from 15.6% at the start of training to 71.0% by the end. More striking was what happened to the model's behavior during this process. The reasoning traces grew substantially longer as training progressed. &lt;/p&gt;

&lt;p&gt;The model spontaneously developed strategies like re-reading the problem from the beginning partway through a solution, checking its own work, and explicitly noting when it suspected an error. None of this was designed in. It emerged from optimizing for correct answers.&lt;/p&gt;

&lt;p&gt;The paper describes a notable emergent behavior where the model, while solving a math problem, pauses mid-reasoning, re-evaluates its approach, and switches to a different strategy to reach the correct answer. This emerges from reward training, where the model learns that revisiting its reasoning can sometimes lead to better outcomes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The training algorithm: GRPO&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The RL algorithm that DeepSeek used (Group Relative Policy Optimization (GRPO)),  is worth understanding, because it's part of why this approach is tractable at scale.&lt;/p&gt;

&lt;p&gt;Standard reinforcement learning for language models (the approach used in earlier RLHF pipelines) depends on Proximal Policy Optimization (PPO). This requires an additional network called a critic, which is basically another large neural network that can estimate the value of partially completed responses. The size of the critic is typically the same as the size of the model that is being trained, resulting in twice the memory/compute requirements of the PPO-based reinforcement learning algorithm, because you must perform two full forward passes for every training step.&lt;/p&gt;

&lt;p&gt;GRPO, first introduced in the DeepSeekMath paper (2024), eliminates the critic. With GRPO, instead of estimating value through a learned value estimation model, GRPO now generates multiple responses for each prompt and receives a reward for each response. This group of multiple responses will provide an average reward, or baseline, for comparison when determining the advantage of any response (i.e., the signal that tells the model whether that particular response was better or worse than what was expected) through the formula:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;A_i = (r_i - mean(r_1...r_G)) / std(r_1...r_G)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is similar to the REINFORCE algorithm from the 90’s, however it is being created on larger scales with modern hardware and still holds the same clipping mechanism found within PPO for training stability. Because of this, you would see how using 50% fewer resources is huge, especially given that your policy model contains 671 billion parameters.&lt;/p&gt;

&lt;p&gt;There were only two main types of rewards that were given in the reward function: correctness rewards (whether or not the final answer matched the ground truth for math problems and/or coding problems) and format rewards (whether or not the model used the structure expected for output). &lt;/p&gt;

&lt;p&gt;There were no process rewards, nor step-by-step supervision. The implicit assumption of this approach was that if you give the model a correct-answer signal, along with a good deal of freedom to explore, the model will be able to find its own way to good reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why R1-Zero wasn't the final model
&lt;/h2&gt;

&lt;p&gt;R1-Zero had a problem because DeepSeek-V3-Base was pretrained on multilingual data, the model sometimes switched languages mid-reasoning. It would start a problem in English, shift into Chinese for a few sentences then continue in English. The reasoning was often correct but the output was unreadable. It also had readability issues. The thinking traces were sometimes clear internal monologues, but at other times, they were nearly incoherent.&lt;/p&gt;

&lt;p&gt;DeepSeek R1 addressed this through a more involved training pipeline. They first collected a small number of high-quality chain-of-thought examples that demonstrated the kind of structured and readable reasoning they wanted. This was used for supervised fine-tuning before RL began, giving the RL process a better starting point. They also added a language consistency reward to penalize mid-reasoning language switching. A subsequent round of rejection sampling and SFT on the RL model's own outputs added coverage for non-reasoning tasks like writing and general question-answering. The result DeepSeek-R1, performs comparably to OpenAI o1-1217 on reasoning benchmarks.&lt;/p&gt;

&lt;p&gt;DeepSeek released smaller models like 7B, 14B, 32B parameters, trained by fine-tuning on reasoning traces generated by the full R1 model. The 32B distilled version outperforms o1-mini on several benchmarks. This is knowledge distillation applied to reasoning i.e, smaller models learn reasoning patterns from larger models, without running the expensive RL training themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling just got a second dimension
&lt;/h2&gt;

&lt;p&gt;The pre-training scaling laws described by Kaplan et al. in 2020 showed a clean relationship, i.e, training compute in, model capability out. The Chinchilla paper (Hoffmann et al., 2022) refined this further, showing that for a fixed compute budget, the optimal strategy is to train a smaller model on more data rather than a larger model on less data. These results organized the field for years.&lt;/p&gt;

&lt;p&gt;Reasoning models introduce a second scaling curve. OpenAI’s o1 blog says performance improves when the model gets more time to think. &lt;/p&gt;

&lt;p&gt;In “The Bitter Lesson” (2019), Richard Sutton makes the case that methods of making use of computing power in a general sense usually outperform in the long term methods that encode human knowledge explicitly. The first instance of this was the scaling of the amount of computational resources utilized for pre-training; the second instance is the scaling of the number of computational resources utilized for inference through the use of learned reasoning. This indicates that we are not done with increasing the scale of AI; in actuality, we have only just entered into a new chapter in that story.&lt;/p&gt;

&lt;p&gt;That being said, scaling with regards to inference does work best for tasks that can have verified answers (i.e., math, formal proofs, code that can be tested). This makes it rather easy to determine whether the answer produced by a model is correct or not (there is either an answer or there is not). &lt;/p&gt;

&lt;p&gt;Extending this to tasks that are open ended (e.g., writing) where there is no clear ground truth is a very active area of research and far more difficult. The current generation of reasoning models is likely very powerful in the STEM and coding fields, although exactly how broadly they will be able to extend beyond these fields remains an active area of research as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  This one is different
&lt;/h2&gt;

&lt;p&gt;There is often a claim every few years that AI has reached a major breakthrough, but it usually turns out to be a small improvement. But this time is different. Now models can use more compute during inference to produce better reasoning, which creates new possibilities that were not available before.&lt;/p&gt;

&lt;p&gt;Tasks that previously needed human intervention because models can’t be trusted to reason carefully, now work differently. You can spend more compute at inference time to get more reliable results. For simple tasks the model can run fast, while harder or more important problems can be given more time to think, making the tool much more flexible than models from a few years ago. &lt;/p&gt;

&lt;p&gt;DeepSeek R1 being released with open weights under an MIT license also changes the economics. The distilled R1-32B model, when used with quantization on a single high-end GPU, performs better than o1-mini on numerical tests. Now that there are multiple APIs, labs, researchers, and smaller organizations with limited resources can take advantage of them. This is a major change in the number of people who would have had access to this type of model before the DeepSeek R1 was made freely available.&lt;/p&gt;

&lt;p&gt;Making models bigger and training them on more data helps them learn more, like reading lots of books makes someone more knowledgeable. But letting models “think longer” when answering questions helps them reason better and make fewer mistakes, like a person taking more time to work through a problem.&lt;/p&gt;

&lt;p&gt;Both of these improvements are important and now we have both. What we do with that combination is still an open question.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2501.12948" rel="noopener noreferrer"&gt;DeepSeek-R1 Paper&lt;/a&gt; — DeepSeek-AI, 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openai.com/index/learning-to-reason-with-llms" rel="noopener noreferrer"&gt;Learning to Reason with LLMs&lt;/a&gt; — OpenAI, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2402.03300" rel="noopener noreferrer"&gt;DeepSeekMath / GRPO Paper&lt;/a&gt; — Shao et al., 2024&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For more such developer content, visit:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;https://vickybytes.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vickybytes</category>
      <category>machinelearning</category>
      <category>deeplearning</category>
      <category>ai</category>
    </item>
    <item>
      <title>Why Terraform Breaks After Day-1 And How Terraform Actions Fix It</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Wed, 01 Apr 2026 08:00:42 +0000</pubDate>
      <link>https://forem.com/shresthapandey/why-terraform-breaks-after-day-1-and-how-terraform-actions-fix-it-435c</link>
      <guid>https://forem.com/shresthapandey/why-terraform-breaks-after-day-1-and-how-terraform-actions-fix-it-435c</guid>
      <description>&lt;p&gt;Let me start with something most infrastructure engineers might not say out loud — Terraform solves Day-1 beautifully and then kinda leaves you hanging.&lt;/p&gt;

&lt;p&gt;You write your HCL, run &lt;code&gt;terraform apply&lt;/code&gt;, and everything is provisioned perfectly. The state file appears impeccable. But six months later that same infrastructure has been poked, patched, manually changed and silently drifted away from what terraform thinks exists. No one realizes this until something breaks in production.&lt;/p&gt;

&lt;p&gt;This article is about that “gap” between provisioning and actually managing infrastructure across its entire lifetime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day-2 Is Where Infrastructure Goes to Die (Slowly)
&lt;/h2&gt;

&lt;p&gt;When a full stack is provisioned onto AWS using Terraform it has a good state and everything is the same, and then after some time passes and a deployment fails, someone logs into the console and changes a security group rule; now the deployment has been successful… but this change has not been documented and no tickets have been raised regarding this change.&lt;/p&gt;

&lt;p&gt;When they run the scheduled &lt;code&gt;terraform apply&lt;/code&gt;, Terraform sees the difference and resets the security group to the original state, resulting in production breaking. Everyone is confused because there were no code changes made.&lt;/p&gt;

&lt;p&gt;The root cause of this issue is that the tools have not been designed for such usage; Terraform's core capability was to provide an infrastructure provisioning capability.&lt;/p&gt;

&lt;p&gt;Therefore, what are teams doing for their Day-2 operations? Most have a combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bash scripts that contain parts nobody understands&lt;/li&gt;
&lt;li&gt;AWS Console changes that are made manually and never documented&lt;/li&gt;
&lt;li&gt;Ad-hoc Ansible runs that don't tie back to Terraform state in any way&lt;/li&gt;
&lt;li&gt;Lambda functions that are each triggering another Lambda function creating a non-traceable chain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In total, over 30 different tools are managing a single hybrid infrastructure estate, which is being actively managed by organizations in the field.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lifecycle Nobody Talks About Enough
&lt;/h2&gt;

&lt;p&gt;Infrastructure has four phases and most of the industry focuses heavily over two of them.&lt;/p&gt;

&lt;p&gt;The first phase, or "Day-0", is the "Build Phase." In this phase an organisation will form their infrastructure and define policies. There has not been any provisioning yet and is done in partnership with the platform and security teams.&lt;/p&gt;

&lt;p&gt;The second phase, or "Day-1", is "Deploy Phase." In this phase &lt;code&gt;terraform apply&lt;/code&gt; will run, infrastructure will be built, and the application teams will deploy their workloads. This is where terraform really starts to show its capabilities.&lt;/p&gt;

&lt;p&gt;Day-2 or "Manage Phase." This phase is where management happens, patches are installed, configurations are changed, certificates are renewed and scaled as needed and where compliance is checked for validity and accuracy. Day 2 can take years to complete and it is also were all of the operational pain will occur. Terraform traditionally has no place in this phase.&lt;/p&gt;

&lt;p&gt;Day-N "Decommission Phase." This phase is where everything is removed and cleaned up.&lt;/p&gt;

&lt;p&gt;Over the last ten years the DevOps industry has been focused on perfecting Day-1 tooling; however, there are very few tools available for Day-2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform Actions — What Changed in v1.14
&lt;/h2&gt;

&lt;p&gt;Terraform Actions were added as stable functionality in Terraform v1.14 and were unveiled at HashiConf 2025. Now, providers can execute an action that does more than just CRUD - calling a lambda function, stopping an EC2, invalidating a CloudFront cache, or triggering an Ansible playbook.&lt;/p&gt;

&lt;p&gt;These new actions are located in their own top-level action block in your HCL. Terraform can automatically execute them based on event triggers during a resource's lifecycle, or they can be invoked manually via the CLI without the need to do a complete &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can invoke an operational action (such as calling a lambda to warm up a cache) without having Terraform re-evaluate the entire state of your infrastructure. This is a significant change in how you will use your infrastructure from now on.&lt;/p&gt;

&lt;p&gt;The AWS provider currently has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;aws_lambda_invoke&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aws_ec2_stop_instance&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aws_cloudfront_create_invalidation&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Actions Actually Work — The Syntax
&lt;/h2&gt;

&lt;p&gt;There are two pieces. The action block itself, and the trigger that fires it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining an Action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_invoke"&lt;/span&gt; &lt;span class="s2"&gt;"warm_cache"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache_warmer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;
    &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform_action"&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;config {}&lt;/code&gt; wrapper. Provider-specific arguments go inside &lt;code&gt;config&lt;/code&gt;, not directly in the action block. &lt;/p&gt;

&lt;p&gt;Meta-arguments like &lt;code&gt;count&lt;/code&gt; and &lt;code&gt;provider&lt;/code&gt; exist outside &lt;code&gt;config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_invoke"&lt;/span&gt; &lt;span class="s2"&gt;"warm_cache"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoke_on_deploy&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;us_east_1&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache_warmer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;
    &lt;span class="nx"&gt;payload&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform_action"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Triggering an Action on Resource Lifecycle Events
&lt;/h3&gt;

&lt;p&gt;This goes inside the resource's &lt;code&gt;lifecycle&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"api"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-api-handler"&lt;/span&gt;
  &lt;span class="c1"&gt;# ... rest of config&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;after_update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_lambda_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warm_cache&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two main things to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;events&lt;/code&gt; uses unquoted keywords — &lt;code&gt;after_create&lt;/code&gt; and &lt;code&gt;after_update&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;actions&lt;/code&gt; is plural and takes a list, not a single reference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also add a &lt;code&gt;condition&lt;/code&gt; to guard the action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ansible_playbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patch_instance&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_auto_patching&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;condition&lt;/code&gt; is false, the action is skipped completely. This is useful when the configuration should exist but only run in certain environments, like production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Actions from the CLI
&lt;/h3&gt;

&lt;p&gt;This is where it gets useful for Day-2 workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Just plan the action, don't run it&lt;/span&gt;
terraform plan &lt;span class="nt"&gt;-invoke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;action.aws_lambda_invoke.warm_cache

&lt;span class="c"&gt;# Actually run the action&lt;/span&gt;
terraform apply &lt;span class="nt"&gt;-invoke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;action.aws_lambda_invoke.warm_cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform only executes that one action. No evaluation or change of any other part of your configuration occurs. Each action can only be executed once at a time; therefore, multiple &lt;code&gt;-invoke&lt;/code&gt; cannot be run in a single command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provisioning EC2 + Immediate Patching via Ansible Automation Platform
&lt;/h2&gt;

&lt;p&gt;One of the most important and widely used use cases is linking EC2 provisioning and automated patching through Ansible Automation Platform (AAP).&lt;/p&gt;

&lt;p&gt;The challenge it solves is simple; there are usually many security patches pending for an Ubuntu AMI that has been provisioned several months prior. If EC2 instances are provisioned, and then you manually take the time to patch each one independently, then at some point (most likely within 30 days) you will not patch an instance you provisioned. Thus, the solution is to link the patching process to the lifecycle of the Terraform instance provisioning so that patching cannot be missed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Terraform Side
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"instance_count"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu_ami"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AMI ID — use a recent Ubuntu LTS, patching will handle the rest"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aap_controller_url"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aap_oauth_token"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"allow_instance_reboot"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"app_servers"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_count&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ubuntu_ami&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.medium"&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_key_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key_name&lt;/span&gt;

  &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allow_ssh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app-server-${count.index}"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;after_update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ansible_aap_job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patch_servers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;after_update&lt;/code&gt; event is critical; should an instance be replaced (due to AMI update, instance type modification, or any reason that would force a new instance to be created), the patching will occur on the newly-created instance automatically without any manual intervention required.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Action Block
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"ansible_aap_job"&lt;/span&gt; &lt;span class="s2"&gt;"patch_servers"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;controller_url&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aap_controller_url&lt;/span&gt;
    &lt;span class="nx"&gt;oauth_token&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aap_oauth_token&lt;/span&gt;
    &lt;span class="nx"&gt;job_template_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EC2 Linux Patching"&lt;/span&gt;
    &lt;span class="nx"&gt;extra_vars&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;vm_hosts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_servers&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;instance_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
          &lt;span class="nx"&gt;public_ip&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;allow_reboot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allow_instance_reboot&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Credentials are stored in HCP Terraform's sensitive variable store. Instance IDs and IPs come straight from resource state at runtime, so AAP always gets current values.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You must refer to your provider documentation to verify the argument names of your AAP action based upon the version you are using, while the structure remains valid.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Ansible Playbook
&lt;/h3&gt;

&lt;p&gt;AAP receives &lt;code&gt;vm_hosts&lt;/code&gt; as an extra variable, builds inventory dynamically, and patches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Patch EC2 Instances&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;
  &lt;span class="na"&gt;gather_facts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

  &lt;span class="na"&gt;pre_tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wait for SSH connectivity&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.wait_for_connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;120&lt;/span&gt;
        &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gather package facts&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.package_facts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apt&lt;/span&gt;

  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update apt package index&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;update_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
        &lt;span class="na"&gt;cache_valid_time&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apply security patches&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;upgrade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dist&lt;/span&gt;
        &lt;span class="na"&gt;only_upgrade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;patch_result&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check if reboot is required&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.stat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/run/reboot-required&lt;/span&gt;
      &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;reboot_required_file&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Reboot if needed and allowed&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.reboot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;reboot_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
        &lt;span class="na"&gt;post_reboot_delay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
      &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;reboot_required_file.stat.exists&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;allow_reboot | default(false) | bool&lt;/span&gt;

  &lt;span class="na"&gt;post_tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Verify instance is up after patching&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.ping&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/var/run/reboot-required&lt;/code&gt; is a file Ubuntu creates automatically when a package update (typically a kernel patch) requires a restart to take effect. The playbook checks for this file rather than blindly rebooting. And even then, it only reboots if &lt;code&gt;allow_reboot&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, which is controlled from your Terraform variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  AAP Job Template Configuration
&lt;/h3&gt;

&lt;p&gt;With respect to Ansible Automation Platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project&lt;/strong&gt; is a reference to the Git repo containing your playbook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory&lt;/strong&gt; is created dynamically from the &lt;code&gt;vm_hosts&lt;/code&gt; value which is assigned to you by Terraform when it runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credentials&lt;/strong&gt; - Your SSH Private RSA key will be stored in AAP's credential vault and will be used to connect to the VM via SSH. This is a very secure separation of the enterprise applications and their configurations (Terraform) and how to connect to them (Ansible).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the Full Workflow Looks Like
&lt;/h2&gt;

&lt;p&gt;An engineer modifies the &lt;code&gt;instance_count&lt;/code&gt; from 2 to 5 and then sends a Git commit with the modification.&lt;/p&gt;

&lt;p&gt;The engineer pushes it to HCP Terraform. HCP Terraform recognizes the alteration made and initiates a plan, which indicates that Terraform will create 3 new AWS EC2 instances; and that Terraform will submit an action request once the provisioned instances are created.&lt;/p&gt;

&lt;p&gt;After an engineer reviews and approves the proposed plan, Terraform executes the apply phase, creating three EC2 instances within AWS. At this time, the &lt;code&gt;action_trigger&lt;/code&gt; will be invoked. Terraform calls AAP's API, sending the newly created instance IDs and public IP addresses, to activate the patching job.&lt;/p&gt;

&lt;p&gt;The AAP will make a dynamic inventory through Terraform and subsequently will wait until all three instances can be accessed through SSH. Once that occurs, AAP will execute an &lt;code&gt;apt dist-upgrade&lt;/code&gt; command to identify whether a reboot is required, and then reboot the instance, if allowed. Finally, AAP will, upon each instance coming back online &amp;amp; responding normally, send a report back to Terraform.&lt;/p&gt;

&lt;p&gt;Upon the completion of the reports from AAP, Terraform will acknowledge completion of the run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Places Actions Are Immediately Useful
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CloudFront invalidation after S3 deployments&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudfront_create_invalidation"&lt;/span&gt; &lt;span class="s2"&gt;"bust_cache"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;distribution_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudfront_distribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;website&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;paths&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_object"&lt;/span&gt; &lt;span class="s2"&gt;"site_bundle"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_cloudfront_create_invalidation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bust_cache&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lambda warm-up after deployments&lt;/strong&gt;: After deployments, cold starts on the first production request are common sources of failures in Lambda functions. First, there may be cold starts on the first production request after deployments, which is typical of failures; hence, the function is invoked immediately after deployment to ensure users do not encounter any faults.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_invoke"&lt;/span&gt; &lt;span class="s2"&gt;"warm_up"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;
    &lt;span class="nx"&gt;payload&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"warmup"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"api_handler"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;after_update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_lambda_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warm_up&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Stopping dev instances on demand&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_ec2_stop_instance"&lt;/span&gt; &lt;span class="s2"&gt;"stop_dev"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;instance_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dev_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply &lt;span class="nt"&gt;-invoke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;action.aws_ec2_stop_instance.stop_dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Chaining multiple actions&lt;/strong&gt; — &lt;code&gt;actions&lt;/code&gt; is a list, order is respected, each one completes before the next starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ansible_aap_job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patch_servers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_lambda_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register_in_cmdb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_lambda_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notify_slack&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Things That Will Catch You Out
&lt;/h2&gt;

&lt;p&gt;An action that fails prevents a run's end:&lt;/p&gt;

&lt;p&gt;By default, how long Terraform waits for an action to finish is determined by the status of the action being waited on. This allows visibility into the status of actions, but introduces potential for issues if AAP has gone down just prior to a critical deployment, preventing Terraform from being able to wait for AAP to complete. Use &lt;code&gt;condition&lt;/code&gt; guards for actions that have minimal impact on the overall deployment if they're interrupted.&lt;/p&gt;

&lt;p&gt;Idempotency is not a luxury:&lt;/p&gt;

&lt;p&gt;Every time there is a resource change, the &lt;code&gt;after_update&lt;/code&gt; event fires. That means that your playbooks and lambda handlers will be invoked multiple times over the lifecycle of your infrastructure. It is acceptable to run &lt;code&gt;apt dist-upgrade&lt;/code&gt; multiple times but not acceptable to perform a database migration multiple times. You must design your programming for re-execution from the very beginning of the process.&lt;/p&gt;

&lt;p&gt;Actions do not write to state:&lt;/p&gt;

&lt;p&gt;When an action is executed there is no record of its execution being written to a statefile in terraform. The only way to tell that the action was executed is from the run history in HCP Terraform and in the logs for any other systems that were involved i.e., AAP job history, cloud watch, etc... You have to plan how you will be able to see/understand when your Terraform works based on its observability functionality.&lt;/p&gt;

&lt;p&gt;The provider support continues to grow:&lt;/p&gt;

&lt;p&gt;v1.14 of the AWS Provider supports a narrow set of operations (action types). Always refer to the Terraform Registry and Provider changelogs prior to assuming any operation is an action.&lt;/p&gt;

&lt;p&gt;CLI invocation requires existing resources:&lt;/p&gt;

&lt;p&gt;If your action references &lt;code&gt;instance.id&lt;/code&gt;, but the instance doesn't exist in state, the &lt;code&gt;-invoke&lt;/code&gt; option will fail during the &lt;code&gt;plan&lt;/code&gt; phase. Actions that use CLI should reference existing resources that have already been provisioned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Actual Shift
&lt;/h2&gt;

&lt;p&gt;Almost all infrastructure management involves Day-2 operations.&lt;/p&gt;

&lt;p&gt;In the past, Day-2 operations were recorded in runbooks, as part of a Jenkins job that only a few people understood, or as a bash script that was modified years ago. Day-2 operations were delivered in a reactive manner, which means if something breaks, somebody performs an action.&lt;/p&gt;

&lt;p&gt;With Terraform Actions, Day-2 operations can now be housed with the infrastructure they manage, from the same repository and pull request workflow and using the same audit trails as the infrastructure they are provisioned on. Patch management will be defined in terms of the infrastructure your organization will provision.&lt;/p&gt;

&lt;p&gt;This kind of change reduces the number of incidents occurring at 2:00 AM.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Terraform Actions is stable from Terraform CLI v1.14.0. Check &lt;a href="https://developer.hashicorp.com/terraform/language/invoke-actions" rel="noopener noreferrer"&gt;developer.hashicorp.com/terraform/language/invoke-actions&lt;/a&gt; for official documentation and your provider's registry page for supported action types.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Technical insights sourced from a community session on Terraform Day-2 operations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For more developer content, visit &lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;vickybytes.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vickybytes</category>
      <category>terraform</category>
      <category>aws</category>
      <category>devops</category>
    </item>
    <item>
      <title>Concurrency is Not Parallelism — And Most Developers Conflate Them</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Wed, 11 Mar 2026 05:33:10 +0000</pubDate>
      <link>https://forem.com/shresthapandey/concurrency-is-not-parallelism-and-most-developers-conflate-them-1d4k</link>
      <guid>https://forem.com/shresthapandey/concurrency-is-not-parallelism-and-most-developers-conflate-them-1d4k</guid>
      <description>&lt;p&gt;There's a quote I keep returning to whenever this topic comes up.&lt;/p&gt;

&lt;p&gt;Rob Pike, one of Go's creators, said it at Heroku's Waza conference back in 2012: &lt;em&gt;"Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This one sentence, fourteen years ago. And I still see developers confusing the two every time.&lt;/p&gt;

&lt;p&gt;I'm not saying that as a criticism — I've mixed them up before too. These two ideas are very close to each other, and in a lot of languages they even use the same tools. On top of that, documentation across the industry has been using the terms interchangeably for years. When languages like Go, JavaScript, Python, Java, and Rust all handle things a little differently and each uses slightly different wording, it’s pretty easy to see why people mix them up.&lt;/p&gt;

&lt;p&gt;So here's my attempt at a proper technical explanation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Here: The Actual Definitions
&lt;/h2&gt;

&lt;p&gt;Concurrency is a structural thing. A program is said to be concurrent if it is written in such a way that many operations can be in progress at the same time. This does not mean that all operations will be in progress at exactly the same time. Instead, it means that the operations can &lt;strong&gt;take turns being in progress&lt;/strong&gt;. This is what concurrency is all about.&lt;/p&gt;

&lt;p&gt;On the other hand, parallelism is all about the &lt;strong&gt;actual execution&lt;/strong&gt; of the tasks and implies that the tasks are being performed at the same time. In fact, parallelism is not possible with a single-core processor.&lt;/p&gt;

&lt;p&gt;The difference between these two terms has been described in the literature of computer science as follows: "Concurrency means that two or more actions are in progress at the same time. Parallelism means that two or more actions are executed at the same moment."&lt;/p&gt;

&lt;p&gt;The term &lt;em&gt;in progress&lt;/em&gt; is of significant importance. It is quite obvious that a task can be &lt;em&gt;in progress&lt;/em&gt; without being actively engaged in the execution of the program. For example, a task may be &lt;em&gt;in progress&lt;/em&gt; while waiting for the result from the database, and another task may be &lt;em&gt;in progress&lt;/em&gt; while engaged in the execution of the program. This is why it is possible for tasks to be &lt;em&gt;concurrent&lt;/em&gt; without them actually executing concurrently.&lt;/p&gt;

&lt;p&gt;Pike's full quote is worth reading once more:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Concurrency is about structure, parallelism is about execution. Concurrency provides a way to structure a solution to solve a problem that may — but not necessarily — be parallelizable."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One thing that people commonly misunderstand is that writing concurrent code does not necessarily mean that it is parallel. You can write code that has many things in progress, but still run it on a single core CPU where only one thing is actually happening at any time. The tasks simply take turns making progress.&lt;/p&gt;

&lt;p&gt;However, the reverse is also true. It is possible that you are dealing with &lt;strong&gt;parallelism without concurrency&lt;/strong&gt;. For instance, you could divide one large calculation into many different CPU processes. The calculation would occur simultaneously. However, there would not be any processes interacting with other processes.&lt;/p&gt;

&lt;p&gt;In other words, concurrency and parallelism are &lt;strong&gt;independent concepts&lt;/strong&gt;. While concurrency and parallelism often occur together in practice, they are not the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Everyone Mixes These Up
&lt;/h2&gt;

&lt;p&gt;First, &lt;strong&gt;threads blur the boundary&lt;/strong&gt;. Concurrency and parallelism can be done using threads. It is possible that the same Thread object could represent processes that alternate on the same processor core, or processes that run concurrently on different processor cores. The programming language syntax for threads is the same in both cases, but the reality depends on the environment in which the code is executed. You can't tell which one you're getting just from the API.&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;documentation is often imprecise&lt;/strong&gt;. If you glance at many references dealing with programming languages, you will find that terms like concurrent execution are used when, in fact, the author means parallel execution, and vice versa. Again, this is not specific to any particular ecosystem.&lt;/p&gt;

&lt;p&gt;Third, &lt;strong&gt;async and await have made the confusion even stronger&lt;/strong&gt;. The terms async and await have gained popularity in languages such as JavaScript, Python, C#, Rust, Swift, and Kotlin. The terms have been used to define a pattern of code that can handle multiple tasks. The confusion arises from the fact that the syntax for the terms looks the same for both I/O-bound concurrency and for speeding up CPU-heavy code, which actually requires parallelism. The code can look identical, even though the runtime behavior is completely different.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four actual combinations
&lt;/h2&gt;

&lt;p&gt;It is helpful to think beyond the notion of concurrency and parallelism as simply true or false. Instead, there are &lt;strong&gt;four different combinations&lt;/strong&gt;, and it is a good thing to know which of the combinations your system actually falls into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrent but not parallel:&lt;/strong&gt; In this case, there are many different tasks that exist at the same time. However, they share the same single CPU core. This means that only one task is ever being executed at any particular time. However, the system will switch between the different tasks so that they can all make progress. A classic example of this is Node.js handling thousands of different HTTP connections on a single thread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel but not concurrent:&lt;/strong&gt; In this case, there is only one task, but that task is split across many different CPU cores. This means that different parts of the task will be executing at the same time. A classic example of this is image rendering. This is one task that is simply split across many different parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrent and parallel:&lt;/strong&gt; There are a number of independent tasks to be executed, and the system is capable of executing several tasks concurrently. A good example is a Go language-based web server that distributes the goroutines to the different CPUs. The requests are defined as independent tasks that can be executed concurrently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Neither concurrent nor parallel:&lt;/strong&gt; This is simple sequential execution. One task runs to completion before the next begins. Many programs start this way, and some remain this way because it keeps the system simple and predictable.&lt;/p&gt;

&lt;p&gt;Most web services will turn out to belong to either the &lt;strong&gt;concurrent only&lt;/strong&gt; group or the &lt;strong&gt;concurrent and parallel&lt;/strong&gt; group. It may make a big difference in your thinking if you know that your system actually belongs to one of these groups.&lt;/p&gt;

&lt;h2&gt;
  
  
  How different runtimes actually do this
&lt;/h2&gt;

&lt;p&gt;These abstract concepts become clearer when you understand how actual runtime environments implement these concepts. Each ecosystem has its own design choices, hence &lt;strong&gt;concurrency can vary significantly depending on the programming language in use&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript / Node.js:
&lt;/h3&gt;

&lt;p&gt;JavaScript is &lt;strong&gt;single threaded&lt;/strong&gt;, meaning that the V8 engine runs the code on a single call stack. It runs the code one frame at a time. This is where the &lt;strong&gt;event loop&lt;/strong&gt; comes in. This is what makes concurrency in JavaScript code possible.&lt;/p&gt;

&lt;p&gt;This is what enables the JavaScript code to perform async operations. This is what the &lt;strong&gt;event loop&lt;/strong&gt; is. It is what makes the JavaScript code perform async operations. For example, if an HTTP request is made, the operation is handed over to the system. This system is &lt;strong&gt;libuv&lt;/strong&gt;. It uses the OS's async I/O. While the operation is being performed, the JavaScript code is still being executed on the call stack. Once the operation is done, the callback is queued, and the event loop runs the operation once the call stack is free.&lt;/p&gt;

&lt;p&gt;Another thing that often catches developers out is the way that &lt;strong&gt;microtasks&lt;/strong&gt; and &lt;strong&gt;macrotasks&lt;/strong&gt; are handled. While &lt;strong&gt;microtasks&lt;/strong&gt;, such as settled Promises and &lt;code&gt;queueMicrotask&lt;/code&gt;, are fully completed before &lt;strong&gt;macrotasks&lt;/strong&gt;, such as &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;setInterval&lt;/code&gt;, and I/O callbacks are executed, that is simply the way the specification is written and is not an error. However, that is often not the way that the developer would have expected things to work, and that is something that needs to be understood if you are going to use JavaScript properly.&lt;/p&gt;

&lt;p&gt;MDN describes the way that the &lt;strong&gt;event loop&lt;/strong&gt; works in the following simple terms: "The &lt;strong&gt;event loop enables asynchronous programming in JavaScript while remaining single threaded&lt;/strong&gt;.”&lt;/p&gt;

&lt;p&gt;Because of the way that the &lt;strong&gt;event loop&lt;/strong&gt; is designed, &lt;strong&gt;Node.js&lt;/strong&gt; is concurrent rather than parallel. It can handle thousands of connections at once because most of those connections are simply waiting. The &lt;strong&gt;event loop&lt;/strong&gt; is very good at handling thousands of waiting connections, but that does not mean that thousands of things are happening at once.&lt;/p&gt;

&lt;p&gt;So, if you need &lt;strong&gt;parallelism&lt;/strong&gt;, you need to use &lt;strong&gt;Web Workers&lt;/strong&gt; in browsers or &lt;strong&gt;Worker Threads&lt;/strong&gt; in Node.js. They provide separate execution contexts in separate OS threads. Workers cannot access shared DOM or shared object references. They can only do it by using &lt;strong&gt;structured cloning&lt;/strong&gt; or &lt;strong&gt;SharedArrayBuffer with Atomics&lt;/strong&gt;. Yes, you need to ask for parallelism in JavaScript.&lt;/p&gt;

&lt;p&gt;This means that &lt;strong&gt;CPU-intensive code in the main thread blocks the event loop&lt;/strong&gt;. It blocks everything else in Node.js from running as long as it is running. &lt;em&gt;async/await&lt;/em&gt; does not change this. &lt;em&gt;Worker Threads&lt;/em&gt; do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// still blocking the main thread, async doesn't help here&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;heavyComputation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// this runs on a separate OS thread&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker_threads&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./heavy-task.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Go:
&lt;/h3&gt;

&lt;p&gt;In the case of the Go language, concurrency is handled as if it were actually a part of the language. This is due to the fact that concurrency is handled via the basic concurrency construct called the &lt;strong&gt;Goroutine&lt;/strong&gt;, which is essentially a lightweight function that can run concurrently. It is to be noted that goroutines are not threads. Instead, goroutines are handled via the Go runtime. This is done via the &lt;strong&gt;M:N Threading Model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The advantage that is gained is that the memory requirements are much lower. This is due to the fact that the goroutine starts off with a stack size of only about &lt;strong&gt;2KB&lt;/strong&gt;, which can increase or decrease dynamically. In the case of threads, however, it is necessary to reserve about &lt;strong&gt;1 to 2 MB&lt;/strong&gt; of memory. This is the reason why it is possible to run hundreds of thousands of goroutines simultaneously without any issues. It would simply not be possible to run that many threads.&lt;/p&gt;

&lt;p&gt;The scheduler in Go uses another algorithm, known as &lt;strong&gt;Work Stealing&lt;/strong&gt;. Each processor, &lt;strong&gt;P&lt;/strong&gt;, has its own queue of goroutines, known as the &lt;strong&gt;run queue&lt;/strong&gt;. If one processor is out of work, it can steal work from another processor’s run queue. The scheduler is also &lt;strong&gt;preemptive&lt;/strong&gt; in Go versions &lt;strong&gt;1.14&lt;/strong&gt; and later. This means that the runtime can stop the execution of the goroutines at safe points, like function calls or loop back edges. This is done to prevent any one goroutine from consuming an OS thread indefinitely.&lt;/p&gt;

&lt;p&gt;When a goroutine is blocked doing &lt;strong&gt;I/O&lt;/strong&gt; or channel receive, it parks, and another goroutine is executed on the same OS thread. The code looks simple, like it is doing a simple sequence of operations, but it is not. The scheduling is going on in the background.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// goroutine parks here, OS thread stays free&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;GOMAXPROCS&lt;/code&gt; controls how many OS threads can run Go code at the same time. Since Go 1.5 it defaults to the number of CPU cores. Goroutines are then distributed across those threads automatically.&lt;/p&gt;

&lt;p&gt;The Go team also pushes a specific coordination philosophy: “Do not communicate by sharing memory; instead, share memory by communicating.” Data moves through channels instead of shared variables. The goal is fewer race conditions by design, not just by careful discipline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python:
&lt;/h3&gt;

&lt;p&gt;Python’s history here is almost a cautionary story. Not because Python is bad, but because it shows what happens when one design decision, made early on, ends up influencing the ecosystem for decades and how hard it is to change that decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The GIL – Global Interpreter Lock&lt;/strong&gt; is a mutex used in the CPython interpreter to ensure that only one thread executes Python code at a time. The number of CPU cores you have can be as many as you want. You cannot have multiple threads of execution running Python code at the same time in the CPython interpreter because of the GIL.&lt;/p&gt;

&lt;p&gt;The reason for the GIL was that the Python interpreter used &lt;strong&gt;reference counting&lt;/strong&gt; for memory management. If the Python interpreter didn’t have the GIL, multiple threads of execution could have potentially updated the reference count of a Python object at the same time, and the memory state of the Python interpreter would have been inconsistent. The GIL ensured that the Python interpreter was simple, stable, and easy to integrate with code written in another language – C. The cost of this simplicity was that Python threads were only ever really useful for &lt;strong&gt;I/O-bound concurrency&lt;/strong&gt;. For CPU-bound concurrency, they didn’t really do anything to help, and they could even have a negative effect.&lt;/p&gt;

&lt;p&gt;So, the ecosystem has developed workarounds. &lt;strong&gt;asyncio&lt;/strong&gt; has become the de facto standard for I/O-bound concurrent code with an event loop, similar to Node.js. &lt;strong&gt;multiprocessing&lt;/strong&gt; has become the standard for CPU-bound code with separate processes, each with their own interpreter and GIL.&lt;/p&gt;

&lt;p&gt;However, all of this is changing with the advent of &lt;strong&gt;Python 3.13 in October 2024&lt;/strong&gt;. &lt;strong&gt;PEP 703&lt;/strong&gt; has introduced an optional "free-threaded build" of CPython, which can run without the GIL. &lt;strong&gt;Python 3.14 in October 2025&lt;/strong&gt; has taken this further by including an optional thread-safe incremental garbage collector, which solves the latency issue in the 3.13 build.&lt;/p&gt;

&lt;p&gt;Removing the GIL wasn’t simple. The approach uses &lt;strong&gt;biased reference counting&lt;/strong&gt;. Each object tracks an owning thread. The owning thread can use fast non-atomic operations, while other threads must use slower atomic ones. This prevents the cache thrashing that would occur if all reference count operations had to be atomic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CPU-bound multithreaded Python code can finally scale across cores&lt;/strong&gt; in the free-threaded build, at times even approaching linear speedups. This is just not possible in the original CPython.&lt;/p&gt;

&lt;p&gt;There is, however, a cost.&lt;/p&gt;

&lt;p&gt;The free-threaded build will be slightly slower for single-threaded code. This is shown by the &lt;em&gt;pyperformance&lt;/em&gt; benchmarks, where the free-threaded build is &lt;em&gt;1% slower on ARM (macOS aarch64)&lt;/em&gt; and &lt;em&gt;up to 8% slower on x86-64 Linux&lt;/em&gt; compared to the normal GIL build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The GIL can also quietly return&lt;/strong&gt;. Note that if you import a C extension module that was not built with free-threading support and is missing the &lt;code&gt;Py_mod_gil&lt;/code&gt; slot, the interpreter will in fact re-enable the GIL instead of crashing. Your code will still work, but it will be serialized again.&lt;/p&gt;

&lt;p&gt;It also requires &lt;strong&gt;explicit installation&lt;/strong&gt;. The default Python build still has the GIL. If you require free threading, you must use the &lt;strong&gt;3.13t or 3.14t builds&lt;/strong&gt;. Library support is still variable.&lt;/p&gt;

&lt;p&gt;The long-term plan is to remove the GIL entirely. This is expected to occur in &lt;strong&gt;Python 3.20&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Java:
&lt;/h3&gt;

&lt;p&gt;Java's original approach was simple: "one Java thread equals one operating system thread." It was solid, it worked, but it didn't scale well. So if your server has 10,000 simultaneous connections, that's 10,000 operating system threads and approximately 10-20 GB of pre-allocated stack memory just for the threads before doing any real work. The solution has been "reactive programming": non-blocking programming frameworks, chains of &lt;code&gt;CompletableFuture&lt;/code&gt;, and reactive streams. They work, and they are the solution, although writing and debugging them can be pretty painful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual threads in Java 21&lt;/strong&gt; - JEP 444, September 2023 - are the solution. Virtual threads are lightweight threads that are managed by the JVM and are multiplexed onto a pool of platform threads. When a virtual thread is blocking due to I/O operations, the virtual thread yields the underlying platform thread, and the JVM immediately runs another virtual thread on that underlying platform thread. Normal sequential blocking code is written while the JVM does the work behind the scenes.&lt;/p&gt;

&lt;p&gt;One detail worth knowing: &lt;strong&gt;virtual thread scheduling is cooperative by default&lt;/strong&gt;. A virtual thread yields when it reaches a blocking operation. If you have a tight CPU loop, it will not yield until something blocks. Go’s &lt;strong&gt;preemptive scheduler&lt;/strong&gt; differs from this in some ways that are important for CPU-bound code.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;CPU-bound parallelism&lt;/strong&gt;, Java uses &lt;strong&gt;ForkJoinPool&lt;/strong&gt; and &lt;strong&gt;StructuredTaskScope&lt;/strong&gt; (preview in Java 21, evolving in later versions) for the distribution of computation over multiple CPUs.&lt;/p&gt;

&lt;h2&gt;
  
  
  I/O-bound vs CPU-bound: the actual decision
&lt;/h2&gt;

&lt;p&gt;When you want to make a program faster, the real question is not “should I use threads?” The real question is &lt;strong&gt;what is actually slow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the task is &lt;strong&gt;I/O bound&lt;/strong&gt;, the program spends most of its time waiting. It could be waiting for a network response, a database query, disk access, or an external API. During that time the CPU is often idle. In these cases &lt;strong&gt;concurrency&lt;/strong&gt; helps because the program can work on other tasks while one task is waiting. This is why platforms like &lt;strong&gt;Node.js&lt;/strong&gt; can handle many connections even with a single thread.&lt;/p&gt;

&lt;p&gt;But if the task is &lt;strong&gt;CPU bound&lt;/strong&gt;, the CPU is already busy doing calculations. Adding more async or concurrency will not make it faster. It mostly helps organize the code. What actually helps in this case is &lt;strong&gt;parallelism&lt;/strong&gt;, where multiple CPU cores work on the problem at the same time.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you're building&lt;/th&gt;
&lt;th&gt;Right tool&lt;/th&gt;
&lt;th&gt;Won't help&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API handling many concurrent requests&lt;/td&gt;
&lt;td&gt;Concurrency — async, event loop, goroutines&lt;/td&gt;
&lt;td&gt;Blocking thread per request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch image or video processing&lt;/td&gt;
&lt;td&gt;Parallelism — Worker Threads, multiprocessing&lt;/td&gt;
&lt;td&gt;Sequential processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fanning out multiple DB queries&lt;/td&gt;
&lt;td&gt;Concurrency — Promise.all, asyncio.gather&lt;/td&gt;
&lt;td&gt;Querying one at a time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML inference on CPU&lt;/td&gt;
&lt;td&gt;Parallelism — multiprocessing, native thread pools&lt;/td&gt;
&lt;td&gt;Python threads pre-3.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chat server with thousands of idle connections&lt;/td&gt;
&lt;td&gt;Concurrency&lt;/td&gt;
&lt;td&gt;One OS thread per connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Video encoding&lt;/td&gt;
&lt;td&gt;Parallelism — frames are independent, distribute them&lt;/td&gt;
&lt;td&gt;Single-threaded encoding&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A server with &lt;strong&gt;10,000 concurrent connections&lt;/strong&gt; does not need 10,000 cores. Most of the connections are just waiting for the server to respond. Concurrency is enough for this problem. A &lt;strong&gt;video encoder&lt;/strong&gt; trying to encode 10,000 videos as fast as possible is a very different problem.&lt;/p&gt;

&lt;p&gt;Same number of tasks on paper, but completely different requirements in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What async/await actually does
&lt;/h2&gt;

&lt;p&gt;This is probably where the most confusion rears its ugly head in real-world development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;async/await&lt;/strong&gt; is just syntax for doing &lt;strong&gt;concurrent I/O-bound code&lt;/strong&gt; but with the illusion of sequential code. When you &lt;strong&gt;await&lt;/strong&gt; something, the task just stops, and other tasks are run. When the task is done, the original task continues. That is it.&lt;/p&gt;

&lt;p&gt;What &lt;strong&gt;async/await&lt;/strong&gt; does not do is give you more CPU cores or allow you to run code in parallel. If you &lt;strong&gt;await&lt;/strong&gt; a &lt;strong&gt;CPU-heavy function&lt;/strong&gt;, the function runs on the same thread and blocks everything else on that thread until the function is done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# concurrent — total time ≈ the slowest single call
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# still blocking the event loop for the entire duration
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;slow&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, simply making the function &lt;strong&gt;async&lt;/strong&gt; and sprinkling &lt;strong&gt;await&lt;/strong&gt; keywords throughout the code does not help CPU-bound code. It is simply an ornament. To handle CPU-bound code in Python, &lt;strong&gt;ProcessPoolExecutor&lt;/strong&gt; is the way to go. The code will run in other processes, and in another process, there is no &lt;strong&gt;GIL&lt;/strong&gt;. &lt;strong&gt;For the free-threaded build of Python 3.14&lt;/strong&gt;, you can use &lt;strong&gt;ThreadPoolExecutor&lt;/strong&gt; too. You will get true parallelism using many threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s changed recently that matters
&lt;/h2&gt;

&lt;p&gt;A few things changed over the last couple of years worth being aware of.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python has finally killed the GIL&lt;/strong&gt;, at least optionally. Python 3.13, October 2024, includes a free-threaded interpreter with the GIL off. Python 3.14, October 2025, took it further. Free threading is now officially supported, not just something you test carefully before shipping. For the first time in the history of Python, threads are capable of running CPU-intensive code in parallel. The path to the GIL being removed as an option, as a default, is planned through Python 3.20.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java 21 virtual threads&lt;/strong&gt; make most of the point of writing reactive Java code for I/O-intensive services irrelevant. One thread per OS thread is no longer a scalability concern. Just write your sequential blocking code, and let the JVM schedule it. For web API services, they are almost I/O bound, so this is a big win.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rust's async story is settled&lt;/strong&gt;, and Tokio is the dominant runtime with a mature ecosystem. Rust's ownership model means data races are a compile-time error, not a runtime surprise. If your systems need to be both correct and fast, Rust is now a viable option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edge runtimes have fundamentally changed the concept of scaling&lt;/strong&gt;. Cloudflare Workers, Deno Deploy, these are all isolated event loop instances running all over the world. You scale by deploying to more machines, not by deploying more threads to one machine. The mind shift of thinking of your server as a function running in 200 different locations at once is a big one.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Rob Pike — "Concurrency is Not Parallelism" — Heroku Waza Conference, January 2012. go.dev/talks/2012/waza.slide&lt;/li&gt;
&lt;li&gt;Rob Pike — "Go Concurrency Patterns" — Google I/O, June 2012. go.dev/talks/2012/concurrency.slide&lt;/li&gt;
&lt;li&gt;MDN Web Docs — "JavaScript execution model" — developer.mozilla.org, updated 2025&lt;/li&gt;
&lt;li&gt;Node.js Documentation — "The Node.js Event Loop" — nodejs.org, 2026&lt;/li&gt;
&lt;li&gt;Go Documentation — Goroutines and Channels — go.dev&lt;/li&gt;
&lt;li&gt;Python PEP 703 — "Making the GIL Optional in CPython" — peps.python.org&lt;/li&gt;
&lt;li&gt;Python 3.14 Docs — "Free Threading" — docs.python.org/3.14&lt;/li&gt;
&lt;li&gt;JetBrains Blog — "Faster Python: Unlocking the GIL" — blog.jetbrains.com, December 2025&lt;/li&gt;
&lt;li&gt;JEP 444 — "Virtual Threads" — openjdk.org, Java 21&lt;/li&gt;
&lt;li&gt;Manning — "Concurrency vs Parallelism" — freecontent.manning.com&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For more such developer content, visit:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;https://vickybytes.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: Edited with AI Assistance &lt;/p&gt;

</description>
      <category>vickybytes</category>
      <category>programming</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>OpenClaw: The AI Agent That Actually Does Stuff - Part 2 - Building a Real Bot</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Fri, 27 Feb 2026 16:08:25 +0000</pubDate>
      <link>https://forem.com/shresthapandey/openclaw-the-ai-agent-that-actually-does-stuff-part-2-building-a-real-bot-26n7</link>
      <guid>https://forem.com/shresthapandey/openclaw-the-ai-agent-that-actually-does-stuff-part-2-building-a-real-bot-26n7</guid>
      <description>&lt;p&gt;If you read &lt;a href="https://dev.to/shresthapandey/openclaw-the-ai-agent-that-actually-does-stuff-part-1-51n4"&gt;Part 1&lt;/a&gt;, you know what OpenClaw is and why it matters. Now we’re going beyond theory. In this part, I’m going to build something real, and show you exactly what happened, including the challenging parts as well, and by the end, you’ll have a working bot that sends you an automated morning dev briefing on Telegram without you doing anything.&lt;/p&gt;

&lt;p&gt;That honesty is the whole point. If you want a tutorial that hides the rough parts, there are many of those. But this is not that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;automated morning briefing&lt;/strong&gt; bot. Every day at 9am, it fetches the top dev and AI stories from web, filters what matters, and sends you a clean summary directly on Telegram. Once you set it up, it just arrives.&lt;/p&gt;

&lt;p&gt;You don’t need any script running in a terminal, or cron job you manually configured. You just need your phone, buzzing with the briefing while you’re still chilling.&lt;/p&gt;

&lt;p&gt;That’s the automation part. That’s what makes this different from a chatbot.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Need Before Starting
&lt;/h2&gt;

&lt;p&gt;Before you start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js v22 or higher:&lt;/strong&gt; run &lt;code&gt;node --version&lt;/code&gt; to check. Update from nodejs.org.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WSL2&lt;/strong&gt; if you're on Windows: native Windows is not supported by OpenClaw, more on this below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Telegram account:&lt;/strong&gt; this is how you talk to your bot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An API key:&lt;/strong&gt; I'll cover the free options below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal comfort:&lt;/strong&gt; you don't need to be an expert but you should be able to run commands without panicking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one is important. OpenClaw is genuinely powerful but it’s not a one-click install. If the terminal feels unfamiliar right now, bookmark this and come back when you’re more comfortable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows? Set Up WSL2 First
&lt;/h2&gt;

&lt;p&gt;OpenClaw doesn't work on native Windows so you need WSL2 — a Linux environment that runs inside Windows. Takes about 5 minutes.&lt;/p&gt;

&lt;p&gt;Open PowerShell as Administrator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--install&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart your machine when it asks. Then open Ubuntu from the Start menu, set up your username and password, and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, run everything in this Ubuntu terminal — not PowerShell, not Command Prompt. Just WSL2 Ubuntu.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API Key Situation
&lt;/h2&gt;

&lt;p&gt;This confused me more than any other part of this build, so I want to help you avoid the same frustration.&lt;/p&gt;

&lt;p&gt;OpenClaw needs an AI model to think, and that model needs API credits. Free tiers on OpenRouter and DeepSeek technically work but they hit limits fast, and with a weak free model the bot sometimes just explains what it would do instead of actually doing it. &lt;/p&gt;

&lt;p&gt;If you can add even $2–5 to OpenRouter or Anthropic, do it. It's enough for weeks of real usage and everything just works properly. With Claude Sonnet, you tell the bot "send me a briefing every morning at 9am" and it saves the scheduled job itself. With a free model you might need to set that config manually — I'll show you how either way.&lt;/p&gt;

&lt;p&gt;My advice: start free to understand how things work. Add credits when you're ready to go fully hands-off. The bot works fine in both cases. The only real difference is how much guidance it needs while you’re setting it up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing OpenClaw
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Need v22.x.x or above&lt;/span&gt;

npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; openclaw@latest
openclaw &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create Your Telegram Bot First
&lt;/h2&gt;

&lt;p&gt;Do this before running the setup wizard so you have the token ready&lt;br&gt;
Telegram → search &lt;code&gt;@BotFather&lt;/code&gt; → send &lt;code&gt;/newbot&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Name it whatever you want, give it a username ending in &lt;code&gt;_bot&lt;/code&gt;. BotFather gives you a token that looks like &lt;code&gt;123456789:ABCdef...&lt;/code&gt;. Copy it right away — you'll need it in the next step.&lt;/p&gt;
&lt;h2&gt;
  
  
  Running the Setup Wizard
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw onboard &lt;span class="nt"&gt;--install-daemon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;--install-daemon&lt;/code&gt; part is important. It sets up the gateway as a proper background service that starts automatically when your machine boots. Without it you'd have to manually start everything every single time.&lt;/p&gt;

&lt;p&gt;Here's what to choose at each screen:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Onboarding mode:&lt;/strong&gt; Manual&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gateway setup:&lt;/strong&gt; Local gateway. You'll see &lt;code&gt;gateway.bind: loopback&lt;/code&gt; on the next screen — this means the gateway is only accessible from your own machine, not your network. Leave it exactly like that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model:&lt;/strong&gt; OpenRouter with &lt;code&gt;openrouter/deepseek/deepseek-chat&lt;/code&gt; if you're going free, or Anthropic with &lt;code&gt;claude-sonnet-4-6&lt;/code&gt; if you have credits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Port and bind:&lt;/strong&gt; just press Enter both times, defaults are fine&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auth mode:&lt;/strong&gt; token. Some older guides still show &lt;code&gt;auth: none&lt;/code&gt; as an option but that got removed after a real security issue was found. Always pick token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tailscale:&lt;/strong&gt; skip it for now. It's for accessing your bot remotely from your phone over the internet. Useful later but adds complexity right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Messaging channel:&lt;/strong&gt; Telegram → paste your BotFather token&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DM policy&lt;/strong&gt; → Pairing. This generates a one-time code that permanently links your Telegram account as the bot owner. Nobody else can use your bot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills:&lt;/strong&gt; skip everything → Finished&lt;/p&gt;

&lt;p&gt;You'll see a huge list of skills — GitHub, Obsidian, Spotify, and a bunch of others. Ignore all of it for now. We're keeping this simple.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pairing Your Telegram Account
&lt;/h2&gt;

&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw tui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Message your bot on Telegram. It'll reply with a pairing code like &lt;code&gt;F85VDARD&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Run this in terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw pairing approve telegram F85VDARD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;confirm&lt;/code&gt; in the TUI. Your account is linked now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Health Check, Then First Message
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this every time you change config. It catches problems early. If no critical errors are showing, then go ahead.&lt;/p&gt;

&lt;p&gt;Now go to Telegram and just send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it replies to you, everything's connected. Gateway, model, Telegram — all working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Bot
&lt;/h2&gt;

&lt;p&gt;Send this in Telegram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;give me a morning briefing with top dev and AI news from https://news.ycombinator.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what actually came back from my bot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Morning Briefing — Top Dev &amp;amp; AI News

1. Google API keys weren't secrets, but then Gemini changed the rules
   Gemini now treats API keys as sensitive, breaking older workflows.
   240 points | 4h ago

2. OpenSwarm – Multi-Agent Claude CLI Orchestrator for Linear/GitHub
   Tool to automate dev workflows using Claude agents.
   18 points | 3h ago

3. Self-improving software won't produce Skynet
   Analysis of why self-modifying code isn't an existential risk.
   13 points | 2h ago
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It browsed Hacker News, filtered what was relevant, and formatted it. I didn't write a single line of code. I sent one message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Automation
&lt;/h2&gt;

&lt;p&gt;Now this is the part that actually makes it an agent.&lt;/p&gt;

&lt;p&gt;Send this in Telegram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;every morning at 9am, fetch top dev and AI stories from [https://news.ycombinator.com](https://news.ycombinator.com/), summarize the most important ones and send me the briefing here on Telegram automatically
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're on a paid model, it saves the scheduled job itself. Check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.openclaw/cron/jobs.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the jobs array is empty because you're on a free model, set it manually. First grab your exact values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#Your username&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;

&lt;span class="c"&gt;#Your timezone&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/timezone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note both outputs. Then run this, replacing the placeholders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"
const fs = require('fs');
const jobs = {
version: 1,
jobs: [
  {
    id: 'morning-briefing',
    schedule: '0 9 * * *',
    timezone: 'YOUR_TIMEZONE_HERE',
    prompt: 'Fetch top 5 dev and AI stories from https://news.ycombinator.com, summarize them concisely and send the briefing to me on Telegram',
    channel: 'telegram',
    enabled: true
  }
 ]
};
fs.writeFileSync('/home/YOUR_USERNAME_HERE/.openclaw/cron/jobs.json', JSON.stringify(jobs, null, 2));
console.log('Done!');
"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_TIMEZONE_HERE&lt;/code&gt; with what &lt;code&gt;cat /etc/timezone&lt;/code&gt; printed — something like &lt;code&gt;Asia/Kolkata&lt;/code&gt;. Replace &lt;code&gt;YOUR_USERNAME_HERE&lt;/code&gt; with what &lt;code&gt;echo $USER&lt;/code&gt; printed.&lt;/p&gt;

&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw gateway restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done. Every morning at 9am the gateway runs the briefing and sends it to your Telegram. You're not doing anything. It just happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do You Need the Terminal Open for This?
&lt;/h2&gt;

&lt;p&gt;No. That's the whole point of &lt;code&gt;--install-daemon&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The gateway is a system service now. macOS runs it via launchd, Linux via systemd. It starts when your machine boots, runs silently in the background, and executes the cron job at 9am whether you're at your desk or not. Close the terminal. Restart your laptop. It doesn't matter.&lt;/p&gt;

&lt;p&gt;The terminal was only for setup. The automation runs without it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Learned
&lt;/h2&gt;

&lt;p&gt;Most tutorials don't include this part. I'm including it because it's the most useful thing I can tell you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which model you use matters a lot.&lt;/strong&gt; With a free model, the bot gets what you're asking for but sometimes it just explains what it would do instead of doing it. With Claude Sonnet it actually does it. OpenClaw being model agnostic is genuinely useful — but that also means the experience is only as good as the model you give it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first Telegram reply is when it clicks.&lt;/strong&gt; Not because it's technically impressive — it's a message, not exactly magic. But because you realize everything is connected and running on your own machine. Your data, your gateway, your agent. That hits different than using some cloud tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's challenging at first and that's okay.&lt;/strong&gt; The setup has rough edges. The free model has real limits. But once you get through it and see how the pieces fit together, you start thinking about what else you could build. That's always a good sign.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Build Next
&lt;/h2&gt;

&lt;p&gt;Once you've got this working, the system is easy to extend.&lt;/p&gt;

&lt;p&gt;Add more sources — dev.to, GitHub release feeds, whatever RSS you follow. Add a Friday evening digest instead of just mornings. Connect the Obsidian skill so summaries go straight into your notes vault. Add weather via wttr.in — no API key needed.&lt;/p&gt;

&lt;p&gt;The skill system is what makes all this composable. Each skill is a SKILL.md file — readable, auditable, nothing hidden. And because OpenClaw uses the same AgentSkills spec as Claude Code, Cursor, and GitHub Copilot, there's a much bigger ecosystem available than just what's on ClawHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  To Wrap Up
&lt;/h2&gt;

&lt;p&gt;OpenClaw is worth the effort. It's not easy to set up and the free model has real limitations. But once it's running, it's genuinely different from everything else out there.&lt;/p&gt;

&lt;p&gt;Start with one workflow, one source, one schedule. Get comfortable with how it behaves. Then expand from there. &lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub + docs: &lt;a href="https://github.com/openclaw/openclaw" rel="noopener noreferrer"&gt;github.com/openclaw/openclaw&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Official site: &lt;a href="https://clawd.bot" rel="noopener noreferrer"&gt;clawd.bot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Security details: &lt;a href="https://docs.openclaw.ai/gateway/security" rel="noopener noreferrer"&gt;docs.openclaw.ai/gateway/security&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Community Discord: &lt;a href="https://discord.gg/openclaw" rel="noopener noreferrer"&gt;discord.gg/openclaw&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Health check: &lt;code&gt;openclaw doctor&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For such articles and developer content, visit:&lt;/strong&gt; &lt;a href="https://vickybytes.com/" rel="noopener noreferrer"&gt;https://vickybytes.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vickybytes</category>
      <category>openclaw</category>
      <category>automation</category>
      <category>ai</category>
    </item>
    <item>
      <title>OpenClaw: The AI Agent That Actually Does Stuff - Part 1</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Tue, 24 Feb 2026 09:30:01 +0000</pubDate>
      <link>https://forem.com/shresthapandey/openclaw-the-ai-agent-that-actually-does-stuff-part-1-51n4</link>
      <guid>https://forem.com/shresthapandey/openclaw-the-ai-agent-that-actually-does-stuff-part-1-51n4</guid>
      <description>&lt;p&gt;Let me be honest with you before we get into anything.&lt;/p&gt;

&lt;p&gt;I’ve been exploring developer tools for a while now and I've gotten pretty good at spotting how the hype cycles work. Something new drops and GitHub, Twitter trends it, and then everyone starts moving on to it. I assumed OpenClaw — or Clawdbot, or Moltbot, whatever name you saw it under — was going to be exactly like that.&lt;/p&gt;

&lt;p&gt;Then I actually sat down, researched it, installed it and spent a proper week using it as a part of my daily workflow.&lt;/p&gt;

&lt;p&gt;And here we are.&lt;/p&gt;

&lt;p&gt;This blog is not just going to be a list of features. I want you to actually understand what OpenClaw is doing differently, how it works, what you can do with it, and what you should know if you’re just getting started with AI agents. By the end, you’ll have enough knowledge to make a decision about whether this actually matters.&lt;/p&gt;

&lt;p&gt;Let’s begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Name Story (As It Changed Three Times)
&lt;/h2&gt;

&lt;p&gt;If you’ve seen Clawdbot, Moltbot, and OpenClaw and thought they were different projects, they’re not. It’s the same project, just renamed over time.&lt;/p&gt;

&lt;p&gt;In November 2025, Austrian developer Peter Steinberger built it as a small weekend experiment and called it &lt;strong&gt;Clawdbot&lt;/strong&gt;, a playful nod to Claude, with a lobster mascot to match. He open sourced it and didn’t expect much.&lt;/p&gt;

&lt;p&gt;In January 2026, it suddenly blew up, gaining around 60,000 GitHub stars in just a few days. Soon after, Anthropic raised a trademark concern because “Clawdbot” sounded too close to “Claude.”&lt;/p&gt;

&lt;p&gt;On January 27, it was renamed &lt;strong&gt;Moltbot&lt;/strong&gt;, keeping the lobster theme. A few days later, the community voted and the final name became &lt;strong&gt;OpenClaw&lt;/strong&gt;, which better reflects what it actually is.&lt;/p&gt;

&lt;p&gt;The current name is OpenClaw. Any tutorial mentioning Clawdbot or Moltbot is referring to the same project.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is OpenClaw?
&lt;/h2&gt;

&lt;p&gt;Currently, most of the AI tools work like chatboxes. You open the tab, type a prompt, get the response and then manually select what you need and where you’re actually doing your work. The AI lives on someone’s server, and you’re the one who connects its output to the real environment.&lt;/p&gt;

&lt;p&gt;OpenClaw takes a completely different approach.&lt;/p&gt;

&lt;p&gt;It’s a free, open-source AI agent you run yourself. Once you set it up, it lives on a machine you control — your laptop, desktop, a cloud VPS, even a Raspberry Pi 4. Once it’s installed and working, you communicate with it through the texting apps you use everyday like WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Microsoft Teams, Google Chat, Matrix, anyone of your choice as it supports all of them.&lt;/p&gt;

&lt;p&gt;Something that distinguishes it, is that it doesn’t only reply with text. It actually does things. It can browse the web, read and write files on your machine, run terminal commands, manage your calendar, draft and send emails, interact with GitHub, fill out forms, and combine all of that together in multi-step workflows that run on their own without manual intervention.&lt;/p&gt;

&lt;p&gt;In Steinberger’s words, it’s “AI that actually does things” — which differentiates it from tools that only explain how to do things.&lt;/p&gt;

&lt;p&gt;Most importantly, your data never leaves your machine until you explicitly allow it to send somewhere. There’s no hidden policy change that suddenly changes how your conversations are handled. Your files, your history, your memory, all of these are stored locally in simple Markdown and YAML files that you can open in any text editor.&lt;/p&gt;

&lt;p&gt;It’s all in your machine, your data, your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Actually Works
&lt;/h2&gt;

&lt;p&gt;This is the most important section, that helps you understand its architecture — why it can do what it does, why security matters, and why it’s built the way it is. So instead of rushing, let’s break it down in a clear, simple way.&lt;/p&gt;

&lt;p&gt;OpenClaw works through three main parts that work together.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Gateway — The Control Plane
&lt;/h3&gt;

&lt;p&gt;The Gateway is a Node.js daemon running constantly in the background, bound to &lt;code&gt;localhost&lt;/code&gt; only by default. It stays connected to all your messaging channels, sends each message to the right agent session, and handles requests one at a time for each session, in the exact order they arrive.&lt;/p&gt;

&lt;p&gt;That detail is more important than it might seem. By handling requests serially, the system avoids race conditions, where multiple tasks running at the same time might conflict over the same files or shared state.&lt;/p&gt;

&lt;p&gt;In addition to handling messages, the system also triggers a heartbeat every 30 minutes by default, or every hour if you're using Anthropic OAuth. This heartbeat allows the agent to check for any scheduled tasks, and perform monitoring on its own, even if you haven’t asked it anything recently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Runtime — The Thinking Loop
&lt;/h3&gt;

&lt;p&gt;The Agent Runtime is the AI loop. &lt;/p&gt;

&lt;p&gt;When a message arrives, the system first loads the session history and any long-term memory saved on disk as plain .md files, so it genuinely remembers what you told it last week. It then adds only the skills that are actually relevant to your current request, instead of loading every installed skill and slowing things down.&lt;/p&gt;

&lt;p&gt;After that, everything is sent to the AI model you have chosen, whether that is Claude, GPT-4o, DeepSeek, or a local model.&lt;/p&gt;

&lt;p&gt;If the model decides it needs to take an action, it sends back a tool call instead of a normal text reply. The system runs that tool, collects the result, and gives it back to the model. The model then decides if it has enough information or if it needs to take another step.&lt;/p&gt;

&lt;p&gt;This process continues until the model reaches a final answer, which is then streamed back to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skills — The Extension System
&lt;/h3&gt;

&lt;p&gt;OpenClaw comes with several built-in tools. It can control a real browser,  which means it can actually click buttons, fill out forms, log in to websites, and interact with pages like a human. It can also read and write files, run shell commands, and handle scheduled jobs.&lt;/p&gt;

&lt;p&gt;On top of these tools, there is ClawHub, which is basically a shared community library of skills created by other users. It includes integrations for tools like Todoist, GitHub, Spotify, Obsidian, Home Assistant, Gmail, Google Calendar, Linear, and many others.&lt;/p&gt;

&lt;p&gt;That said, ClawHub is community-contributed and lightly vetted — more on this in the security section below.&lt;/p&gt;

&lt;p&gt;Skills themselves are simple. Each one is mainly a &lt;code&gt;SKILL.md&lt;/code&gt; documentation file, sometimes it includes an optional install script. The agent reads the documentation to understand what the skill can do, similar to onboarding a new teammate by giving them clear written instructions, instead of making them go through code to figure things out.&lt;/p&gt;

&lt;p&gt;Another important detail is that the skill format OpenClaw also works with the AgentSkills spec, an open standard adopted by Claude Code, Cursor, VS Code, GitHub Copilot, and others. So you are not limited to just the skills available on ClawHub. It connects to a much larger ecosystem, giving you access to many more existing skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Actually Build With It
&lt;/h2&gt;

&lt;p&gt;Here are some workflows developers are actually running:&lt;/p&gt;

&lt;p&gt;Repo management from your phone. It pushes to branches, check test results, review recent commits, from anywhere without your laptop. If you have ever been traveling or commuting when something urgent happened in a repo, you already understand how useful this is.&lt;/p&gt;

&lt;p&gt;Overnight autonomous coding loops. At the end of the day, you describe what needs to be done, for example, refactor the auth module, write tests, and open a PR. By morning, the pull request is ready, along with the agent’s reasoning written in the comments. The documentation recommends using Claude for more complex, multi-step coding tasks because it handles reasoning better and is more resistant to prompt injection. Sonnet 4.6 is suggested when you want to manage costs, and Opus 4.6 when you need stronger performance.&lt;/p&gt;

&lt;p&gt;Proactive information briefings. You can connect skills to sources like GitHub issues, Hacker News, subreddits, or RSS feeds and track keywords related to your work. The agent filters the content, summarizes what matters, and sends you a digest at whatever schedule you choose. This works through the heartbeat system, so it runs automatically without you having to trigger it each time.&lt;/p&gt;

&lt;p&gt;Voice notes into structured notes. Send a voice message through Telegram, and the agent will transcribe it, pull out the key points, and save it to any tool you prefer. This process takes about 15 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Matters Even If You’re Just Starting Out
&lt;/h2&gt;

&lt;p&gt;Let me be honest about this. OpenClaw is not beginner friendly. You need Node.js v22 or higher, you should be comfortable using the terminal, and you need some basic understanding of networking. The documentation clearly says that if you are not confident with command line tools, this is not something you should run casually. That is not gatekeeping, it is just being realistic about the risks.&lt;/p&gt;

&lt;p&gt;That said, it is still worth learning about early in your AI journey.&lt;/p&gt;

&lt;p&gt;One big reason is transparency. OpenClaw is MIT licensed and fully open. You can read the code yourself. You can see how the Gateway handles and queues requests. You can inspect how skills are discovered and loaded. You can open the memory files on disk and see exactly what is stored. You can even look at how the system prompt is constructed before every LLM call. Here, nothing is a black box unless you choose not to look.&lt;/p&gt;

&lt;p&gt;That level of visibility helps you build a clear mental model of how agent systems actually work. You start to understand the moving parts, how memory is managed, how tools are selected, how decisions flow from one step to the next. And that understanding is not limited to this one project. As AI agents become more common in real software workflows, having that foundation will matter.&lt;/p&gt;

&lt;p&gt;If the terminal still feels intimidating, that is fine. Save it for later and revisit it when you feel more comfortable. The setup process will likely improve over time. But even before you run it yourself, the concepts behind it are worth understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security: The Non-Negotiable Part
&lt;/h2&gt;

&lt;p&gt;OpenClaw has wide access. It can read and write files, run shell commands, connect to messaging apps, check your calendar, and much more. But that level of access means you need to be careful with how you set it up, thus security is not optional here.&lt;/p&gt;

&lt;p&gt;Network access: By default, the Gateway only binds to localhost, which is good. If you want to access it in your phone, use an SSH tunnel or Tailscale, which the project recommends. Do not expose port 18789 directly to the public internet. That is asking for trouble.&lt;/p&gt;

&lt;p&gt;Authentication setup: The &lt;code&gt;auth: none&lt;/code&gt; option was permanently removed in version 2026.1.29 after a real vulnerability was discovered. Crafted links could redirect your auth token to an attacker’s server, and the WebSocket server was not validating Origin headers properly.&lt;/p&gt;

&lt;p&gt;If you see any guide that still shows this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Removed — do not use

auth: none

# Use one of these instead

auth: token    # openclaw gateway token
auth: password # OPENCLAW_GATEWAY_PASSWORD=yourpassword
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update immediately if you are running an older version. Do not continue using outdated configs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ClawHub skills:&lt;/strong&gt; These are contributed by the community and are only lightly vetted. Before installing anything, read the SKILL.md file and any install.sh script. It just takes two minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt injection risks:&lt;/strong&gt; The system prompt guardrails are helpful, but they are not hard security boundaries. Real protection comes from locking down your inbound channels. Use allowlists where possible. Enable mention gating in group chats. Treat links and file attachments from unknown sources as potentially malicious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run openclaw doctor regularly:&lt;/strong&gt; There is a built in diagnostic command called openclaw doctor. Run it from time to time. It helps surface configuration mistakes early, before they turn into bigger security issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;What you need before installing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js v22+ (node --version)&lt;/li&gt;
&lt;li&gt;Terminal comfort (WSL2 on Windows — native Windows not supported)&lt;/li&gt;
&lt;li&gt;API key for your model (Claude recommended; GPT-4o or DeepSeek work too)&lt;/li&gt;
&lt;li&gt;Ideally a separate machine or cheap VPS (2GB RAM minimum, ~$4–5/month on Hetzner or DigitalOcean)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g openclaw@latest
openclaw onboard --install-daemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--install-daemon&lt;/code&gt; flag installs the Gateway as a proper system service. On macOS it uses launchd, and on Linux it uses systemd. That means it will automatically start when your machine boots up. Do not skip this step, otherwise you will have to manually start it every time.&lt;/p&gt;

&lt;p&gt;The setup wizard guides you through the whole process. It helps you configure authentication, initialize your workspace, connect your first messaging channel, and install your first skill.&lt;/p&gt;

&lt;p&gt;If you are just getting started, use Telegram as your first channel. It is the simplest to set up. WhatsApp is supported too, but the linking process is more involved and can be a bit tricky the first time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended model config:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-sonnet-4-6&lt;/span&gt;    &lt;span class="c1"&gt;# good balance of capability and cost&lt;/span&gt;
  &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-4o&lt;/span&gt;
  &lt;span class="c1"&gt;# For complex autonomous tasks: claude-opus-4-6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openclaw doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you finish the setup, run this once. And make it a habit to run it again anytime you change the configuration.&lt;/p&gt;

&lt;p&gt;In the beginning, keep things simple. Start with one clear workflow, maybe a Telegram research assistant or a basic morning task manager. Use it for a while and understand how it responds, how it uses tools, and how it handles memory.&lt;/p&gt;

&lt;p&gt;Once you feel confident about how it behaves, then slowly expand its access and add more capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It's Headed
&lt;/h2&gt;

&lt;p&gt;On February 14, 2026, Steinberger announced that he is joining OpenAI and moving OpenClaw under an open source foundation. It will remain independent from any single company, stay MIT licensed, and operate with proper governance. The codebase is not going closed. It remains fully open.&lt;/p&gt;

&lt;p&gt;For something that is only a few months old, that kind of direction is rare. This does not look like a weekend side project that will fade away quietly. The core ideas behind it, being local first, model agnostic, messaging native, truly agent driven, and fully open, are closely aligned with where modern developer tools are heading.&lt;/p&gt;

&lt;p&gt;Even if you are not planning to install it right now, it is worth understanding what it represents and how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub + docs: &lt;a href="https://github.com/openclaw/openclaw" rel="noopener noreferrer"&gt;github.com/openclaw/openclaw&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Official site: &lt;a href="https://clawd.bot/" rel="noopener noreferrer"&gt;clawd.bot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Security details: &lt;a href="https://docs.openclaw.ai/gateway/security" rel="noopener noreferrer"&gt;docs.openclaw.ai/gateway/security&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Community Discord: &lt;a href="https://discord.gg/openclaw" rel="noopener noreferrer"&gt;discord.gg/openclaw&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Health check: &lt;code&gt;openclaw doctor&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For such articles and developer content visit: &lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;https://vickybytes.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vickybytes</category>
      <category>openclaw</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>What 20-Year Tech Veterans Said About Developer Skills for 2026</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Mon, 16 Feb 2026 17:13:21 +0000</pubDate>
      <link>https://forem.com/shresthapandey/what-20-year-tech-veterans-said-about-developer-skills-for-2026-2i5b</link>
      <guid>https://forem.com/shresthapandey/what-20-year-tech-veterans-said-about-developer-skills-for-2026-2i5b</guid>
      <description>&lt;p&gt;A few days back, I participated in a competition supported by &lt;strong&gt;VickyBytes&lt;/strong&gt;. That experience completely changed my perspective.&lt;/p&gt;

&lt;p&gt;Between the rounds, I had an opportunity to speak with several tech professionals, each having over 20 years of industry experience. Their valuable insights on emerging technologies changed the way I used to think. They didn't just work in the industry, they witnessed multiple technology shifts over their careers.&lt;/p&gt;

&lt;p&gt;When I asked them, "If you were starting today in 2026, what would you actually focus on?"&lt;/p&gt;

&lt;p&gt;What they told me was much more specific than what I expected. From these discussions, 10 technologies emerged as the most important for developers in 2026.&lt;/p&gt;

&lt;p&gt;Here's what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Agentic Orchestration &amp;amp; MCP
&lt;/h2&gt;

&lt;p&gt;Developers are now expected to build multi-agent systems using the Model Context Protocol (MCP) to integrate LLM models with various tools and data sources. It represents a major change from building a single AI chatbot to designing systems where multiple agents collaborate to perform specific tasks.&lt;/p&gt;

&lt;p&gt;The Model Context Protocol provides an organized way for modern AI models to communicate with tools and data sources. Instead of writing custom integration codes for each tool, you implement MCP server once and any MCP compatible AI can utilize it.&lt;/p&gt;

&lt;p&gt;This includes designing systems where different agents handle different tasks. For example: one agent monitors data stream, another analyzes patterns, third communicates with APIs, and fourth makes decisions based on input. &lt;/p&gt;

&lt;p&gt;The technical challenge comes with orchestration. It involves knowing when agents should work in parallel, managing context windows across agents, handling errors, and debugging systems where behaviour depends on the interactions between agents.&lt;/p&gt;

&lt;p&gt;Positions like “AI Agent Architect” and “Multi Agent Systems Engineer” are now appearing with competitive salary ranges, reflecting the shift in job market.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Rust
&lt;/h2&gt;

&lt;p&gt;Rust has become an essential language for performance-critical and blockchain applications, mainly because of its memory-safety guarantees, as memory safety bugs are not acceptable in many fields.&lt;/p&gt;

&lt;p&gt;The ownership system is a feature that defines Rust. Every part of memory has exactly one owner, and as soon as the owner goes out of scope, the memory is released automatically. To share data, you either transfer ownership or let someone borrow it for a defined period. And when it comes to changing data across threads, safety at compile-time must be ensured.  &lt;/p&gt;

&lt;p&gt;Something that makes it stand out is that all categories of bugs like use-after free, buffer overflows, null pointer dereferences, become compile-time errors instead of runtime failures. The language is built to keep these issues out of the compiled code.&lt;/p&gt;

&lt;p&gt;The learning process can be challenging. Initially, you might struggle with the compiler and try to understand its error messages. As time passes, things start to make sense. Everything feels clearer, and you start thinking about ownership and borrowing more naturally.&lt;/p&gt;

&lt;p&gt;The job market is strong. Roles in blockchain, embedded systems, cloud infrastructure, and gaming now specify Rust as a requirement, senior positions often come with high pay. Learning Rust’s memory model helps you write better code in other languages as well because it makes memory management clearer and more intentional.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Retrieval-Augmented Generation
&lt;/h2&gt;

&lt;p&gt;Expertise in vector databases like Pinecone or Weaviate, along with building retrieval pipelines, has become important for creating AI systems that use real-time and private data. RAG helps reduce AI hallucinations by making the model base its answers on real data instead of depending only on its training data.&lt;/p&gt;

&lt;p&gt;RAG architecture involves multiple components. First, documents are divided into chunks (around 200-500 words each) and then converted into vector embeddings using models that capture their meaning. These embeddings are stored in vector databases that are built to find similar content quickly, even across large amounts of data.&lt;/p&gt;

&lt;p&gt;When a query comes in, it gets converted into an embedding using the same model and the vector database searches the most similar chunks. Those chunks are sent to the model as context along with the query, so it can generate an answer based on real data. &lt;/p&gt;

&lt;p&gt;Retrieval pipelines often need to be hybrid because pure semantic search can miss exact matches while pure keyword search can miss conceptual relationships. Strong systems usually combine both, with a reranking step using a smaller model to improve the final results.&lt;/p&gt;

&lt;p&gt;Choosing a vector database comes with tradeoffs. Managed solutions like Pinecone are easier to use, while self-hosted options like Weaviate or Qdrant give more control and can be cheaper at large scale. But the hard part isn’t only the tools, it’s understanding how embeddings work and how to write good prompts for RAG. Systems should be designed to recognize when the retrieved context isn’t enough, instead of giving wrong answers confidently.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Platform Engineering 2.0
&lt;/h2&gt;

&lt;p&gt;Developers are moving from traditional DevOps to Internal Developer Platforms (IDPs). IDPs allow self-service infrastructure and also include AI-driven protection mechanisms to prevent mistakes. This is the point where infrastructure is no longer something that every developer understands and interacts with, but rather a product that can be easily consumed.&lt;/p&gt;

&lt;p&gt;The aim is to simplify the infrastructure complexity and still retain the flexibility. Developers should be able to deploy their services easily without having to know the details of Kubernetes. Monitoring should already be read to use. Security and compliance should be part of the system by default.&lt;/p&gt;

&lt;p&gt;Strong platforms give developers self-service tools that already follow company’s best practices. Instead of providing cloud credentials and documentation to developers, you give them simple interfaces that guide them for right choices and make incorrect choices difficult.&lt;/p&gt;

&lt;p&gt;Integration of AI guardrails is something that’s evolved in 2026. Platform teams are now creating systems to manage AI usage. They handle things like prompt management, rate-limiting for LLM calls, and preventing sensitive data from reaching external APIs.&lt;/p&gt;

&lt;p&gt;This also applies to model deployment. Fine-tuned models are versioned and deployed using the same pipelines as application code. A/B testing is built into the platform, and monitoring keeps track of errors and code automatically. If certain limits are passed, the system can rollback changes on its own.&lt;/p&gt;

&lt;p&gt;This is a mix of having strong infrastructure skills and a product mindset. You are creating services that other engineers use on a daily basis. Thus, it is important to understand how these services are used, where they fail, and how you can improve them based on usage patterns. It is not just about the technology, but whether people are using the platform and whether it helps them move faster.&lt;/p&gt;

&lt;p&gt;Platform engineering roles show up more often in job listings, and they often pay more than senior backend roles. The reason is simple: a small platform team can boost the productivity of the entire organization, making a much bigger impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Go (Golang) for Cloud-Native
&lt;/h2&gt;

&lt;p&gt;Go is still a top choice for building microservices and Kubernetes tools because it handles concurrency in a scalable way. Most of the cloud-native world like Kubernetes, Docker, Terraform, is built with Go, so if you work in cloud infrastructure, you’re very likely to run into it.&lt;/p&gt;

&lt;p&gt;The main strength of Go lies in goroutines, making concurrency simple. Instead of managing threads or dealing with complex patterns, you start a goroutine for each task. The Go runtime handles the complex part, distributing thousands of goroutines across a small number of operating system threads.&lt;/p&gt;

&lt;p&gt;This makes it much easier to build services that handle multiple requests at the same time. The code looks simple and organized, even while managing thousands of operations. The garbage collector is designed for low delays, even when the system is under heavy load. &lt;/p&gt;

&lt;p&gt;The language is very simple. There’s no complex inheritance, no operator overloading and very little hidden behaviour. This keeps code easy to read even months or years later, and helps new members become productive faster.&lt;/p&gt;

&lt;p&gt;The tooling is strong and consistent. go fmt removes arguments about code style. go test handles testing and benchmarking. go build creates a single static binary with no runtime dependencies, making deployment much easier. &lt;/p&gt;

&lt;p&gt;The ecosystem is well established. There are solid web frameworks, database drivers, gRPC support, and a reliable standard library. In most cases, what you need is already there, so you’re not constantly searching for missing pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. TypeScript &amp;amp; Type-Safe Frontends
&lt;/h2&gt;

&lt;p&gt;TypeScript has become a key part for building enterprise-grade web applications, with frameworks like Next.js and NestJS leading the way. What began as “JavaScript with types” is now the standard choice for serious web development.&lt;/p&gt;

&lt;p&gt;The most important advantage is that errors can be detected at compile time, not at runtime. Type checking ensures that functions are called properly, that properties of objects exist, and that values such as null or undefined are handled intentionally.&lt;/p&gt;

&lt;p&gt;The ecosystem has consolidated around TypeScript. The tools are designed to integrate smoothly with TypeScript. Capabilities such as accurate auto-completion, refactoring, and documentation become more accurate with the presence of type information in the code.&lt;/p&gt;

&lt;p&gt;Types can be used to reflect business rules, making sure certain values aren’t mixed up, that some actions only work on validated data, and that API responses match the expected structure. Editor integration changes how you write code. Autocomplete doesn’t just suggest function names, it also shows parameter types and return values. &lt;/p&gt;

&lt;p&gt;For teams, the advantages build over time. Types make the intent of the code clearer, which speeds up the reviews. Junior developers get quick feedback from the compiler, instead of finding errors later in production.&lt;/p&gt;

&lt;p&gt;The switch doesn’t have to happen all at once. You can move from JavaScript to TypeScript one file at a time and tighten the rules gradually. There’s no need to commit to full strict mode on day one. The job market shows the dominance of TypeScript. Most senior frontend roles now expect TypeScript experience. It’s not a niche skill anymore, it’s simply the baseline.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Shift-Left Security (DevSecOps)
&lt;/h2&gt;

&lt;p&gt;Security is now integrated early in the development process. Developers must be proficient in automated threat modeling and secure code development right in their IDEs. This is a paradigm shift from security being the final gate to security being a natural part of the development process.&lt;/p&gt;

&lt;p&gt;This approach adds security checks throughout the development process. IDEs flag potential vulnerabilities as you code. Pre-commit hooks catch problems before they reach version control, and CI/CD pipelines run deeper scans before code review. Security becomes ongoing feedback instead of last-minute check.&lt;/p&gt;

&lt;p&gt;Modern tools like Secret scanners catch API keys, passwords, or tokens in commits and history. Dependency scanners watch for known vulnerabilities and can automate updates. Static analysis tools spot issues like SQL injection or XSS directly in the code.&lt;/p&gt;

&lt;p&gt;Secure-by-default libraries make safety part of everyday coding. Database query builders block SQL injection by design, and HTTP clients manage authentication and rate limits automatically. Using these tools means security happens naturally, as part of normal development.&lt;/p&gt;

&lt;p&gt;The business case is clear. Fixing security problems after code is in production is far more expensive than catching them early. Data breaches can lead to huge costs from remediation, fines and lost customer trust. Investing in security tools is small compared to the potential losses.&lt;/p&gt;

&lt;p&gt;Security champions are developers trained in security who act as points of contact for the security team and help spread best practices across larger teams. Instead of the security team reviewing everything, each development team has someone with deeper security knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. WebAssembly (WASM)
&lt;/h2&gt;

&lt;p&gt;Languages such as C++ and Rust can now run almost as fast as native code in browsers. This opens the way for complex web-based AI and gaming applications. WebAssembly enables developers to bring their code and performance-critical paths to the web.&lt;/p&gt;

&lt;p&gt;The speed is quite remarkable. For heavy computations like 3D graphics, video encoding, scientific simulations, and cryptography, WebAssembly offers speeds that are almost comparable to native code, all while staying safely within the browser boundaries.&lt;/p&gt;

&lt;p&gt;Real-world use cases demonstrate this capability. Graphics design software, CAD tools, and image editors now run inside browsers with speeds that previously required desktop applications. Even game engines compile to WebAssembly, providing speeds almost comparable to console-quality performance right inside the browser.&lt;/p&gt;

&lt;p&gt;The security architecture is sound. WASM runs in the same sandbox as JavaScript, with no filesystem or network access unless explicitly granted. Unlike JavaScript, WASM modules explicitly list their imports and exports, which makes it easier to reason about their capabilities.&lt;/p&gt;

&lt;p&gt;In practice, WASM takes care of the heavy lifting, and JavaScript handles the UI and platform interactions. The separation is clean, with WASM exporting functions for computation and JavaScript providing access to browser APIs.&lt;/p&gt;

&lt;p&gt;The tooling has improved a lot. Emscripten compiles C and C++ to WASM with strong browser support. Rust has first-class WASM support with excellent tools, and other languages are gradually adding WASM targets as well.&lt;/p&gt;

&lt;p&gt;There are some limitations. WASM can’t access the DOM directly, so JavaScript is needed for UI work. Working with mixed WASM and JavaScript projects also means managing two languages and separate build systems.&lt;/p&gt;

&lt;p&gt;Beyond performance, WASM provides a secure sandbox for running untrusted code, and plugin systems can leverage WASM to add extensibility while keeping strong security boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Telemetry &amp;amp; Observability Engineering
&lt;/h2&gt;

&lt;p&gt;The focus in modern systems is moving beyond basic logging toward full observability, often using OpenTelemetry and AI-driver debugging. Traditional logs just aren’t enough for today’s complex distributed systems.&lt;/p&gt;

&lt;p&gt;Observability is built on structured telemetry: metrics to show what is happening, traces to show how requests are flowing through the system, and logs to provide additional information. These three pillars must be unified so that events can be correlated across the whole system.&lt;/p&gt;

&lt;p&gt;The model is now becoming proactive instead of reactive. Instead of looking back at failures after they happen, observability must call attention to problems as they are happening, warning of a degradation of performance before it becomes an outage.&lt;/p&gt;

&lt;p&gt;OpenTelemetry allows for standardized instrumentation and data formats, making telemetry platform-agnostic and not vendor-lock-in. Context propagation, trace IDs, and additional context passed through all services is critical to reconstructing the whole picture of request processing.&lt;/p&gt;

&lt;p&gt;The most important tool is understanding what to observe. Too much telemetry generates noise, unnecessary logs or uninstrumented metrics, while good observability is centered on the signals that indicate real issues or insights into system behaviour.&lt;/p&gt;

&lt;p&gt;Instrumentation is now a core developer responsibility. Developers must decide which metrics matter, structure traces to provide actionable insights, and write logs that help debugging rather than adding unnecessary data.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. FinOps for Developers
&lt;/h2&gt;

&lt;p&gt;With the volatility of AI and cloud costs, developers now need to write code with costs in mind and include budget checks in their deployment processes. Infrastructure costs aren’t just an operations issue anymore, they’re part of development.&lt;/p&gt;

&lt;p&gt;The problem is that cloud costs can change and are hard to predict. Running AI models can become very expensive as usage grows. Auto-scaling can add resources quickly to handle load, which can lead to higher bills.&lt;/p&gt;

&lt;p&gt;Cost-aware development means knowing how technical choices affect money. Picking a database isn’t only about features, it’s about understanding how much it will cost. Choosing compute resources means balancing speed and performance against budget limits.&lt;/p&gt;

&lt;p&gt;Good teams include cost checks in their development pipeline. Tools can estimate costs before deployment. Some AI model are much more expensive than others, and this matters when millions of requests are involved.&lt;/p&gt;

&lt;p&gt;Auto-scaling must be set up carefully. Fast scaling reacts to traffic quickly but can raise costs a lot. Slow scaling saves money but can hurt performance. The right setup balances speed and cost while monitoring both metrics.&lt;/p&gt;

&lt;p&gt;Communication is as important as skills. Programmers must be able to tell other people why some features are more expensive. The ability to estimate costs before building them helps to prioritize work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Common Thread
&lt;/h2&gt;

&lt;p&gt;These ten technologies share a common characteristic: they exist to handle complexity. Modern systems are distributed, AI dependent, need strong security, and use a lot of resources. Old ways of building software can’t always keep up with these demands.&lt;/p&gt;

&lt;p&gt;The change isn’t just about new tools, it’s about how software is created. Security has to be ongoing, not just a final step. Costs should be planned during the start, not added after issues appear. AI systems need careful orchestration, not just simple prompting. &lt;/p&gt;

&lt;p&gt;These changes aren’t short-term trends. They reflect a deeper shift in how software is designed, built, and managed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;These technologies can feel overwhelming at first. The key is to focus: pick one out of them that fits your current work. If you’re building AI systems, try RAG or MCP. For infrastructure work, look into Go or Platform Engineering. If you work with web apps, focus on TypeScript and security tools.&lt;/p&gt;

&lt;p&gt;Start by building something simple. Learning through tutorials is one thing, but learning through experience is what builds actual skills. Choose a project where you apply the technology to solve actual problems. You will encounter real-world problems, make errors, and learn through debugging.&lt;/p&gt;

&lt;p&gt;Learn from people who have already worked with these technologies. Seeing why they made certain choices, what issues they faced, and what they’d do differently gives insights that documentation alone can’t provide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Technology keeps changing fast. These ten technologies matter because they solve real problems in large-scale production systems. They represent a new way of thinking about software, not just new tools in the old way of working.&lt;/p&gt;

&lt;p&gt;The skills you gain from learning them go beyond the tools themselves. Understanding Rust’s ownership model, improves memory management thinking in any language. Working with RAG systems builds knowledge in information retrieval and prompt design, useful across AI projects. Experience in Platform Engineering applies whenever you need to improve developer workflows.&lt;/p&gt;

&lt;p&gt;These technologies aren’t about keeping up with trends, they’re about giving you the skills to build the systems that matter today and in the future.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Edited with AI assistance.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>career</category>
      <category>developer</category>
    </item>
  </channel>
</rss>
