<?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>DEV Community: Kostya Malinovskiy</title>
    <description>The latest articles on DEV Community by Kostya Malinovskiy (@kostya_malinovskiy_7f732b).</description>
    <link>https://dev.to/kostya_malinovskiy_7f732b</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%2F3342961%2F69499514-797e-4be8-b0a0-984b2ff4a59b.png</url>
      <title>DEV Community: Kostya Malinovskiy</title>
      <link>https://dev.to/kostya_malinovskiy_7f732b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kostya_malinovskiy_7f732b"/>
    <language>en</language>
    <item>
      <title>You Probably Don’t Need an AI Coding Course</title>
      <dc:creator>Kostya Malinovskiy</dc:creator>
      <pubDate>Wed, 01 Apr 2026 11:58:51 +0000</pubDate>
      <link>https://dev.to/kostya_malinovskiy_7f732b/you-probably-dont-need-an-ai-coding-course-3g9o</link>
      <guid>https://dev.to/kostya_malinovskiy_7f732b/you-probably-dont-need-an-ai-coding-course-3g9o</guid>
      <description>&lt;p&gt;Recently I’ve seen many blog posts and videos about &lt;strong&gt;AI coding workflows&lt;/strong&gt; — how people build them, how productive they are, and often a mention that a course is available (or coming soon) to teach the same system.&lt;/p&gt;

&lt;p&gt;I think it’s great that people share their knowledge and experience with fellow developers.&lt;/p&gt;

&lt;p&gt;At the same time, I believe much of this knowledge is &lt;strong&gt;not that hard to obtain on your own&lt;/strong&gt;, especially given how powerful modern AI tools are for learning.&lt;/p&gt;

&lt;p&gt;Before buying a course, it may be worth trying to explore the topic yourself first.&lt;/p&gt;

&lt;h2&gt;
  
  
  My approach
&lt;/h2&gt;

&lt;p&gt;When I want to quickly understand a new topic, I often use a &lt;strong&gt;“Fisherman’s prompt”&lt;/strong&gt; — a prompt template designed to generate structured learning paths.&lt;/p&gt;

&lt;p&gt;You can find the template here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/disler/cad56b41fb614a4e394f7b719bca5273" rel="noopener noreferrer"&gt;https://gist.github.com/disler/cad56b41fb614a4e394f7b719bca5273&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(There is a lot of great material in that repository.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For example, here is a prompt I used to explore &lt;strong&gt;AI-augmented coding workflows&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I'm a Senior software Engineer and I want to learn AI augmented coding so I can utilize Open Claude to help me with engineering. Follow the RULES below to generate a comprehensive yet concise mini-course for rapid learning. The course should contain chapters that teach me about these SUB_TOPICS. Make sure the chapters fit my level, profession and topic. Ask for clarification if you need more information about my knowledge.

SUB_TOPICS

- Mental Models of LLMs for Engineers
- Effective Claude Code Usage for Software Development
- Context Engineering (efficient context management)
- Task Decomposition for AI Agents
- Designing Agent Skills (Markdown capability files)
- Sub-agents and Task Delegation
- Agent Loops (ReAct / Reflection / Self-correction)

RULES

- Use concrete examples to explain every concept
- Use emojis to add expression
- Generate one chapter at a time
- Ask for feedback at the end of each chapter
- Ask if I need clarification at the end of each chapter
- With each concept, explain real world use cases
- After we complete every sub topic, present a list of additional topics we can explore that align with my level, profession, topic and objective.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also adjust the topic list depending on what interests you most.&lt;/p&gt;

&lt;p&gt;Using the generated course as a starting point, together with resources like the Anthropic skills repository, &lt;a href="https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md" rel="noopener noreferrer"&gt;https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md&lt;/a&gt; &lt;br&gt;
 gives you a &lt;strong&gt;very fast way to get hands-on experience&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;From there, experiment with workflows that work for you, iterate on them, and refine your approach.&lt;/p&gt;

&lt;p&gt;After that, you’ll have a much clearer understanding of the field — and you’ll know whether you actually need a course or not.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Pipes Won't Let Me Go</title>
      <dc:creator>Kostya Malinovskiy</dc:creator>
      <pubDate>Wed, 07 Jan 2026 15:42:35 +0000</pubDate>
      <link>https://dev.to/kostya_malinovskiy_7f732b/pipes-wont-let-me-go-16pl</link>
      <guid>https://dev.to/kostya_malinovskiy_7f732b/pipes-wont-let-me-go-16pl</guid>
      <description>&lt;p&gt;Some time ago when I was working on codecrafters shell challenge I hit a problem.&lt;br&gt;
The problem was that behaviour of my shell if use it via terminal was different from when use it in test environment.&lt;br&gt;
When use it via terminal emulator the prompt was shown, however in tests it wasn't there. It appeared that readline works a bit different if communicating with it throug Stdin Stdout programatically.&lt;br&gt;
I managed to overcome the problem, i wrote about it in one of my previous posts, but i think it is quite interesting and important to understand how this 2 ways work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is tty?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In unix system everything is a file including any device, tty is not an exception. It is a special character device. It means that it is a device that it streaming data as it receives it in real time byte by byte.&lt;/p&gt;

&lt;p&gt;As it is a file it can be open for read or read with &lt;code&gt;cat&lt;/code&gt;, or it can be written to with &lt;code&gt;echo &amp;gt;&lt;/code&gt;. There is something confusing though. Suppose we use &lt;code&gt;/dev/ttyp0&lt;/code&gt; we want to write some data to it so it would be picked up somwhere, also we want to receive feedback from the device. But there seems to be a problem, if it is a file, how what is written on our hand is not mixing up with what is sent from the other hand?&lt;/p&gt;

&lt;p&gt;At this point I found that instead of saying "in unix everything is a file" it would be better to say: "in unix everything looks like a file". What it means that file and tty device have same interface: read, write, close, but for diferent things it behaves differently differently. You see file descriptor no only a pointer to a file, it includes some additional info, and among other things it includes info on what drivers to use. For File read and write will use same destination, for tty read and write would use different buffers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Two-Way Pipe&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TTY it is a 2 way-pipe (full-duplex). When any tty device would be represented by master end and slave end - two different entries at &lt;code&gt;/dev/ptmx&lt;/code&gt; and &lt;code&gt;/dev/pts/4&lt;/code&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Master end is held by usef facing app, when app receives typed charecters it writes them to master. &lt;/li&gt;
&lt;li&gt;Slave end is held by a shell application and used slave as a stdin, and stdout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When something is written to master, it appears on slave reader, and directed to a shell app stdin. When a shell app writes something to its stdout, whidh is slaves write, it is transfered to masters read. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TTY vs PTY&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TTY is actual hardware(e.g &lt;code&gt;/dev/ttys0&lt;/code&gt;), so the other end of the pipe is actual device&lt;/li&gt;
&lt;li&gt;PTY is a pseudo-terminal, it is pair of files used to build TTY like interaction between software apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also such thing as line discipline, this is a kernel layer that echoes typed characters back to master, buffers input until Enter is hit. It also intercepts special symbols and key sequences like &lt;code&gt;^c&lt;/code&gt; or &lt;code&gt;^z&lt;/code&gt; and act on them accordingly. While this is an important piece, I will not focus on it at this post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;how to find out if app interacts with tty?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;it can be easialy done with &lt;code&gt;IsTerminal(fd int)&lt;/code&gt; from "golang.org/x/term", but how does it work under the hood?&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="c"&gt;// x/term/term.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;IsTerminal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&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;isTerminal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// x/term/unix_term.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;isTerminal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_&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;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IoctlGetTermios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ioctlReadTermios&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;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="c"&gt;// x/term/term_unix_bsd.go&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ioctlReadTermios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TIOCGETA&lt;/span&gt;
&lt;span class="c"&gt;// x/sys/unix/ioctl_unsigned.go&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;IoctlGetTermios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="kt"&gt;uint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Termios&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;Termios&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;ioctlPtr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unsafe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;ioctlPtr&lt;/code&gt; makes a syscall with a file descriptor and &lt;code&gt;unix.TIOCGETA&lt;/code&gt; request, and writes result to value, it errors if file under &lt;code&gt;fd&lt;/code&gt; is not tty. Presnse of error in the upstream code is interpreted as current file descriptor does not point to a tty device.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;unix.TIOCGETA&lt;/code&gt; is read:&lt;br&gt;
TIO - terminal Input/Output&lt;br&gt;
C - control&lt;br&gt;
GET - read/retrieve operation&lt;br&gt;
A - attributes&lt;/p&gt;

&lt;p&gt;Actual &lt;code&gt;unix.TIOGETA&lt;/code&gt; value of course is not an arbitrary integer. But as I think it would be to deep in the rabbit hole for now, and I'll get to it somewhere in the future.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>go</category>
      <category>learning</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>Pipes</title>
      <dc:creator>Kostya Malinovskiy</dc:creator>
      <pubDate>Tue, 30 Dec 2025 19:57:05 +0000</pubDate>
      <link>https://dev.to/kostya_malinovskiy_7f732b/pipes-5bbi</link>
      <guid>https://dev.to/kostya_malinovskiy_7f732b/pipes-5bbi</guid>
      <description>&lt;p&gt;In my journey to master Go my next objective was to update my shell to be able to handle commands that look like:&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;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; test.log | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First idea was to collect command 1 output and then invoke command 2 with the collected output as argument. But as you may notice command 1 is &lt;code&gt;tail -f&lt;/code&gt; which does not exit on its own and it means that it would block the execution.&lt;/p&gt;

&lt;p&gt;More over if you try to test it:&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;echo &lt;/span&gt;line1 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; test.log
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ./test.log | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then in another terminal window you try&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;echo &lt;/span&gt;line2 &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ./test.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see that the &lt;code&gt;line2&lt;/code&gt; was printed out. It could mean that &lt;code&gt;head -3&lt;/code&gt; does not wait for &lt;code&gt;tail -f test.log&lt;/code&gt; to exit and to collect its output, instead it receives the output and prints it realtime.&lt;br&gt;
Then if you read &lt;a href="https://www.gnu.org/software/bash/manual/bash.html#Pipelines" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; you'll find confirmation for this hypothesis:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The output of each command in the pipeline is connected via a pipe to the input of the next command. That is, each command reads the previous command’s output.&lt;/p&gt;

&lt;p&gt;Each command in a multi-command pipeline, where pipes are created, is executed in its own subshell, which is a separate process&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then if in another terminal window you try:&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;echo &lt;/span&gt;line3 &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ./test.log echo line4 &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ./test.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you'll see that your command printed out &lt;code&gt;line3&lt;/code&gt; and exited.&lt;/p&gt;

&lt;p&gt;What happens here is:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On the third write to test.log, &lt;code&gt;tail&lt;/code&gt; picks up the line and writes to stdout, which is the write end of the pipe &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;head&lt;/code&gt; receives the line through its stdin, the read end of the pipe. &lt;/li&gt;
&lt;li&gt;As &lt;code&gt;head&lt;/code&gt; was instructed to print only 3 lines, it exits and closes its end of the pipe &lt;/li&gt;
&lt;li&gt;On the 4th write, &lt;code&gt;tail&lt;/code&gt;  tries to write to stdout, but fails to do so as other end of the pipe is closed. This triggers a SIGPIPE signal, which terminates &lt;code&gt;tail&lt;/code&gt;.  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With all this understanding what can be done in Go to mimic the behavior? &lt;/p&gt;

&lt;p&gt;Create the commands:&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="n"&gt;cmd1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"./test.log"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cmd2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"head"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect the commands:&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="n"&gt;outPipe&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;cmd1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StdoutPipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cmd2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;outPipe&lt;/span&gt;
&lt;span class="n"&gt;cmd2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="c"&gt;// the go program stdout which is the terminal where it was executed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the commands:&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="n"&gt;cmd1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cmd2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
 &lt;br&gt;
&lt;code&gt;func (*Cmd) Start()&lt;/code&gt; start command processes and doesn’t wait for them to finish.  Wait for the commands to finish:&lt;br&gt;
&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="n"&gt;cmd2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="c"&gt;// remember head exits before tail&lt;/span&gt;
&lt;span class="n"&gt;cmd1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;…and it appears that &lt;code&gt;tail&lt;/code&gt; never exits… &lt;/p&gt;

&lt;p&gt;This is where I learned an interesting thing. You see, &lt;code&gt;exec.Command&lt;/code&gt; spawns a child process, which receives its own copies of all File descriptors of original process. So when &lt;code&gt;head&lt;/code&gt; command finishes, it closes it stdin, &lt;strong&gt;but its original in the parent process(my program) remain open&lt;/strong&gt;. This means that in order to close the read end of the pipe I also have to close it in the parent process.&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="n"&gt;cmd2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;outPipe&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;cmd1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whole program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import(
    "fmt"
    "os/exec"
    "os"
)

func main(){    
    cmd1 := exec.Command("tail", "-f", "/Users/skuf_love/study/go/test_pipes/test.log")
    cmd2 := exec.Command("head", "-n", "5")
    outPipe, err := cmd1.StdoutPipe()
    if err != nil {
        fmt.Printf("Err1: %q", err)
    }
    cmd2.Stdin = outPipe
    cmd2.Stdout = os.Stdout

    err = cmd1.Start()
    if err != nil {
        fmt.Printf("cmd1 start error: %v\n", err)
    }
    err = cmd2.Start()
    if err != nil {
        fmt.Printf("cmd2 start error: %v\n", err)
    }

    cmd2.Wait()
    fmt.Println("cmd2 wait done")
    outPipe.Close()
    cmd1.Wait()
    fmt.Println("cmd1 wait done")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>cli</category>
      <category>go</category>
      <category>linux</category>
    </item>
    <item>
      <title>Reading readline</title>
      <dc:creator>Kostya Malinovskiy</dc:creator>
      <pubDate>Mon, 22 Dec 2025 13:13:48 +0000</pubDate>
      <link>https://dev.to/kostya_malinovskiy_7f732b/reading-readline-10gl</link>
      <guid>https://dev.to/kostya_malinovskiy_7f732b/reading-readline-10gl</guid>
      <description>&lt;p&gt;CodeCrafters verifies each challenge step by running test suit on their side on each push to the main branch. The problem with that is that for free membership tests are not always run instantly and can end up waiting in queue. In addition, even if test runs start right away, they still take considerable amount of time. After I started working on the Shell challenge, I quickly realized that I need to have local test suit. I found that Golang provides testing infrastructure right out of the box, so started building on top of it.  For go test, you need to implement at least one function with name Test - example. In my case I wanted to have an integration tests, so I needed some additional setup around. Go testing package allows you to do so by implementing TestMain function, that will run once per suite run.&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;TestMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;)&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;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"go"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"testapp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed to build app: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"testapp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exitCode&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;Here I build my shell app, run the suite and remove the app afterwards.&lt;/p&gt;

&lt;p&gt;Then in a test function I have to run the shell, get stdin, stdout and stderr handlers to be able to send commands to my shell and receive and verify output. I’ll omit these details as it is not the subject of this blog.&lt;/p&gt;

&lt;p&gt;Initially I implemented shell as a for loop that runs indefinitely, print prompt, read user input and print the output like this:&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;for&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;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$ "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;input&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;rl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Readline&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;out&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;executeCmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&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="n"&gt;out&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;Then in test setup I do:&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="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./testapp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// create cmd Struct for my shell&lt;/span&gt;
&lt;span class="n"&gt;stdout&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;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StdoutPipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// connect to the shell stdout pipe&lt;/span&gt;
&lt;span class="n"&gt;stdoutReader&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bufio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// wrap stdout pipe with the buffered reader, to get ReadString(), ReadByte() methods&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;br&gt;
Then in tests I used &lt;code&gt;bufio.ReadString('$')&lt;/code&gt; to get the tested command output, as I knew that &lt;code&gt;$&lt;/code&gt; means that the new prompt is there and all output has already been printed. Of course I would be in trouble if a tested command output would include &lt;code&gt;$&lt;/code&gt; somewhere in the middle, but as I control command output and test data, this thing was working for me. &lt;/p&gt;

&lt;p&gt;It was fine until I reached the step where in the challenge description it was recommended to use &lt;code&gt;readline&lt;/code&gt; library. This is when my tests started to fail, It appeared that readline doesn’t print prompt when enclosing app is not used through TTY.&lt;/p&gt;

&lt;p&gt;My tests were hanging as ReadString was blocking execution because it was waiting for &lt;code&gt;$&lt;/code&gt; to appear in the shell stdout, which was not happening.&lt;/p&gt;

&lt;p&gt;After several hours of trial and error I came up with idea to read output by byte in a goroutine, send each byte through a channel to the  calling function. In its turn the calling function would return whatever it had collected to the upstream code on a timeout. But I had to be sure that the goroutine finished before next shell command is tested, otherwise there is a risk that it will start collecting input from the next command and stealing it from the proper reader groutine. To solve this I decided to sent some special character to the shell stdin so it would appear in the stdout, and groutine could read it and know that is has to exit. At first I tried to send &lt;code&gt;\x04&lt;/code&gt; which means EOT(end of transmission), and felt semantically correct. By doing so I learned 2 things: 1. I needed to use &lt;code&gt;echo&lt;/code&gt; so something could get to stdout 2. \x04 was read by readline(or is by something else), and was interpreted as signal to finish execution. &lt;br&gt;
Instead I went with echoing &lt;code&gt;%&lt;/code&gt;, and It worked for me. Again if a command output includes &lt;code&gt;%&lt;/code&gt; this would cause me troubles, but I’m ok with such a tradeoff while I’m in control of test data. When it will not be the case I’ll figure out better solution.  Here’s how function to get a command output looks:&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;ShellTestContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ReadUntilPrompt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Builder&lt;/span&gt;

    &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
    &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StdoutReader&lt;/span&gt;
    &lt;span class="n"&gt;buf&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;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;done&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="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"About to start goroutine"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Goroutine started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;b&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;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadByte&lt;/span&gt;&lt;span class="p"&gt;()&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&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;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Inside goroutine error: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Inside goroutine closing the channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadByte&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// read \n after %&lt;/span&gt;
                &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;b&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="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;anotherByte&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteByte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anotherByte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;220&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&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;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Timeout goroutine, received result: %v"&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="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"echo %&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// use % symbol to signal goroutine to stop reading and finish&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;nil&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;P.S.&lt;br&gt;
While putting these post together I learned that technick with looking for specific character is called Sentinel. Also I learned that TTY or not can be determinded to by inspectint file descriptor that is used for STDIN, STDOUT or STDERR. It can be done via syscall or via &lt;a href="https://pkg.go.dev/golang.org/x/term#IsTerminal" rel="noopener noreferrer"&gt;https://pkg.go.dev/golang.org/x/term#IsTerminal&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>learning</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>Learning Go the Hard Way</title>
      <dc:creator>Kostya Malinovskiy</dc:creator>
      <pubDate>Mon, 22 Dec 2025 08:04:33 +0000</pubDate>
      <link>https://dev.to/kostya_malinovskiy_7f732b/learning-go-the-hard-way-1ia</link>
      <guid>https://dev.to/kostya_malinovskiy_7f732b/learning-go-the-hard-way-1ia</guid>
      <description>&lt;p&gt;So, I’ve recently found myself with some free time and the opportunity to tackle my "backlog of wannados."&lt;br&gt;
At the top of that list is Golang. I believe diving into it will broaden my engineering outlook and deepen my systems-level expertise.&lt;/p&gt;

&lt;p&gt;Why "The Hard Way"? In my experience, the best way to truly internalize new knowledge is to struggle and hit the wall. You have to fail first to create a "knowledge vacuum" that pulls the skills in.&lt;/p&gt;

&lt;p&gt;To make this happen, I'm starting with the Shell Challenge at &lt;a href="https://codecrafters.io/" rel="noopener noreferrer"&gt;CodeCrafters.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;"Hard Way" in my world looks this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚫 No AI coding assistants. &lt;/li&gt;
&lt;li&gt;🚫 No LSP for navigation or completions. I’m going to do everything by hand to build muscle memory and be more mindful of how I organize my code.&lt;/li&gt;
&lt;li&gt;📖 Docs First. The official documentation is my primary source. LLMs are the absolute last resort when nothing else makes sense.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expect several wheels to be reinvented and plenty of bad practices to be implemented along the way. It’s all intentional—a way to gain a deep, firsthand understanding of why best practices and patterns exist in the first place.&lt;/p&gt;

&lt;p&gt;I’ll be posting updates here about the specific problems I struggle with most.&lt;br&gt;
See you around!&lt;/p&gt;

&lt;p&gt;P.S. I’m not an AI doomer. I think LLMs are great tools for building things efficiently. However, when the goal is learning, they often stand in your way by providing shortcuts where there should be deep thought.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>programming</category>
      <category>learning</category>
      <category>go</category>
    </item>
  </channel>
</rss>
