<?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: Victoria Drake</title>
    <description>The latest articles on DEV Community by Victoria Drake (@victoria).</description>
    <link>https://dev.to/victoria</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%2F19058%2Fbc69a0ce-7996-4e5e-94ae-978856cfd14f.png</url>
      <title>DEV Community: Victoria Drake</title>
      <link>https://dev.to/victoria</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/victoria"/>
    <language>en</language>
    <item>
      <title>I Spent $78 Learning Why Bash Still Matters in the AI Age</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Tue, 19 Aug 2025 12:00:00 +0000</pubDate>
      <link>https://dev.to/victoria/i-spent-78-learning-why-bash-still-matters-in-the-ai-age-4417</link>
      <guid>https://dev.to/victoria/i-spent-78-learning-why-bash-still-matters-in-the-ai-age-4417</guid>
      <description>&lt;p&gt;Here's how a little laziness cost me $78.&lt;/p&gt;

&lt;p&gt;While working on a personal project recently, I wanted Cline to process about a hundred files that were each in subdirectories of a project. I fired up Cline and picked Gemini 2.5 Pro (context window FTW) and asked it to recurse through the subdirectories, process the files, and put the results in a new file.&lt;/p&gt;

&lt;p&gt;Cline got to work… slowly. I watched as the "API Request…" spinner appeared for each file read and each time it saved the results. About twenty minutes and $26 later, it finished.&lt;/p&gt;

&lt;p&gt;Okay, I thought, that's not great, but not untenable. The cost of convenience, right? I opened up the results file to take a look and.. &lt;em&gt;sigh&lt;/em&gt;. Not great work. It was obvious that some files had been skipped despite my very careful instructions to process each and every one.&lt;/p&gt;

&lt;p&gt;So, like a glutton for punishment, I made a list of the files Cline had skipped and asked it to try again. Tired of babysitting, I raised the "Maximum Request Auto Approval" limit to more than I thought would be needed to finish processing the files that were left, and went to take a coffee break.&lt;/p&gt;

&lt;p&gt;When I came back, Cline was done. The results? Still not great. Files had still been skipped, some files that were processed were missing results, and, oh, my task bill had risen to $78.&lt;/p&gt;

&lt;p&gt;Okay, &lt;em&gt;this&lt;/em&gt; was untenable. Reading all this data into context was costly and slow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1568411307907-18b502a750ce%3Fixlib%3Drb-4.0.3%26auto%3Dformat%26fit%3Dcrop%26w%3D800%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1568411307907-18b502a750ce%3Fixlib%3Drb-4.0.3%26auto%3Dformat%26fit%3Dcrop%26w%3D800%26q%3D80" alt="Coffee cup splashing" width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then the coffee started to kick in, I guess, because it dawned on me: why in the world was I using expensive API calls to do something a Bash one-liner could do?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Cline, write a Bash command that will recurse through the &lt;code&gt;data/&lt;/code&gt; directory and obtain the content of all the files and copy it into a single new file."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which produced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find data/ &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; + &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; all_data.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;find data/&lt;/code&gt; - searches recursively in the &lt;code&gt;data&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-type f&lt;/code&gt; - specifies that we're looking for files only (not directories, links, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-exec cat {} +&lt;/code&gt; - for all files found, execute the &lt;code&gt;cat&lt;/code&gt; command. The &lt;code&gt;{}&lt;/code&gt; is a placeholder for the filename, and the &lt;code&gt;+&lt;/code&gt; is a crucial optimization that groups multiple filenames into a single &lt;code&gt;cat&lt;/code&gt; command, avoiding the overhead of launching a new process for every single file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt; all_data.txt&lt;/code&gt; - redirects the standard output of the &lt;code&gt;cat&lt;/code&gt; command (which is the concatenated content of all the files) into a new file named &lt;code&gt;all_data.txt&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I asked Cline to read the resulting &lt;code&gt;all_data.txt&lt;/code&gt; file, process it, and output the results.&lt;/p&gt;

&lt;p&gt;It took about two minutes.&lt;/p&gt;

&lt;p&gt;And it cost me $0.78.&lt;/p&gt;

&lt;h2&gt;
  
  
  What just happened?
&lt;/h2&gt;

&lt;p&gt;My initial naive approach had accidentally created a perfect storm of computational inefficiency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1558494949-ef010cbdcc31%3Fixlib%3Drb-4.0.3%26auto%3Dformat%26fit%3Dcrop%26w%3D800%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1558494949-ef010cbdcc31%3Fixlib%3Drb-4.0.3%26auto%3Dformat%26fit%3Dcrop%26w%3D800%26q%3D80" alt="Network cables" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When Cline processed each file individually, it was making separate API calls for every single operation - reads, writes, the works. With about 100 files, that meant roughly 200+ API calls, each one spinning up its own network round-trip with all the latency that entails. Every time I saw that "API Request…" spinner, I was watching money float away into the ether.&lt;/p&gt;

&lt;p&gt;But here's the kicker: large language models like Gemini charge based on token consumption.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's not just the file content they're charging for; every single API call also included the entire conversation history, system prompts, and my instructions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With a stateless API, that context has to be re-transmitted with every single request. If my average context was around 10,000 tokens and I made 200 calls, I burned through 2 million tokens (10,000 * 200) on overhead alone, before even counting the actual data.&lt;/p&gt;

&lt;p&gt;Combining all the files with bash flipped this whole equation on its head. Instead of 200 API calls, I made exactly one. Instead of bearing the network latency for every file operation, combining the files locally on my machine meant the filesystem could actually optimize that work. What had taken almost an hour of network round-trips for Gemini to access all the data was reduced to a couple hundred milliseconds of local file operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The expensive lesson in algorithmic thinking
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1635372722656-389f87a941b7%3Fixlib%3Drb-4.0.3%26auto%3Dformat%26fit%3Dcrop%26w%3D800%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1635372722656-389f87a941b7%3Fixlib%3Drb-4.0.3%26auto%3Dformat%26fit%3Dcrop%26w%3D800%26q%3D80" alt="Mathematical equations on chalkboard" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This whole debacle reminded me why understanding the cost model of your tools matters just as much as understanding their capabilities. API pricing is designed around per-request and per-token charges, which naturally punishes fine-grained operations. It's similar to how databases are optimized for bulk operations rather than processing individual rows - the overhead of each transaction quickly becomes the bottleneck.&lt;/p&gt;

&lt;p&gt;My first approach had O(n) complexity for API calls, where n equals the number of files. The bash solution reduced that to O(1) by batching everything locally first. That's the difference between linear scaling and constant cost, and at $78, I felt every bit of that mathematical distinction.&lt;/p&gt;

&lt;p&gt;There's also something to be said about data locality here. My original method couldn't take advantage of any local caching or filesystem optimizations. Every operation had to go over the network to an API server, get processed, and come back. The bash approach kept everything local until the very end, letting my machine's filesystem cache work its magic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real cost of convenience
&lt;/h2&gt;

&lt;p&gt;I'd fallen into the trap of thinking that because I &lt;em&gt;could&lt;/em&gt; use an AI tool for everything, I &lt;em&gt;should&lt;/em&gt; use it for everything. But there's a difference between leveraging AI for tasks that require intelligence and using it as an expensive replacement for basic system utilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1586864387967-d02ef85d93e8%3Fixlib%3Drb-4.0.3%26auto%3Dformat%26fit%3Dcrop%26w%3D800%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1586864387967-d02ef85d93e8%3Fixlib%3Drb-4.0.3%26auto%3Dformat%26fit%3Dcrop%26w%3D800%26q%3D80" alt="Everything is a nail" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The irony is that I probably spent more mental energy managing and troubleshooting the AI approach than I would have just thinking through the problem for five minutes and reaching for the right tool from the start. Sometimes the most sophisticated solution is knowing when to employ a basic tool.&lt;/p&gt;

&lt;p&gt;My little bit of laziness bought me a $78 lesson that boils down to this: always understand the economic model of your tools, especially when they're priced per operation. The most elegant and cost-effective solution isn't always the newest and most technically exciting one.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>webdev</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Create Better Code Documentation 10x Faster with AI</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Tue, 12 Aug 2025 12:00:00 +0000</pubDate>
      <link>https://dev.to/victoria/create-better-code-documentation-10x-faster-with-ai-505a</link>
      <guid>https://dev.to/victoria/create-better-code-documentation-10x-faster-with-ai-505a</guid>
      <description>&lt;p&gt;Documentation has always been one of those "we should do this" tasks that somehow never makes it to the top of the sprint. But what if creating comprehensive, useful documentation could be as straightforward as explaining your code to a colleague?&lt;/p&gt;

&lt;p&gt;Conversational AI has changed the game entirely. Instead of starting with a blank page and trying to remember every detail a new team member might need, you can have AI help you think through the process systematically. The result isn't just better docs—it's documentation that actually serves your team's needs as you grow and evolve.&lt;/p&gt;

&lt;p&gt;Here's how to use AI to build documentation that scales with your team and genuinely improves how you work together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation That Welcomes New Team Members
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyr0k4976p2ilcqj3o7p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyr0k4976p2ilcqj3o7p.jpg" alt="Welcome sign" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The best part about using AI for documentation is that it naturally thinks from an outsider's perspective. While you and your team already understand your system's quirks and design decisions, AI starts fresh every time—much like a new hire would.&lt;/p&gt;

&lt;p&gt;Most conversational AI tools allow you to upload code files or paste code snippets. You can then use prompts that help surface the knowledge your team takes for granted:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write documentation for a new software engineer joining our team. Assume they're experienced but know nothing about our specific domain, architecture decisions, or business logic. Include the "why" behind non-obvious technical choices and flag anything that might seem strange or unexpected to an outside developer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This approach reveals the implicit knowledge that experienced team members forget to document—why certain patterns exist, what alternatives were considered, and where the potential gotchas are. It transforms documentation from a chore into a useful onboarding tool that actually reduces the time senior developers spend answering questions.&lt;/p&gt;

&lt;p&gt;To create comprehensive documentation you can use immediately, provide the AI with additional context such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the application does and who uses it&lt;/li&gt;
&lt;li&gt;Key architectural decisions and their reasoning&lt;/li&gt;
&lt;li&gt;Setup and deployment processes&lt;/li&gt;
&lt;li&gt;Integration points with other systems&lt;/li&gt;
&lt;li&gt;Common troubleshooting scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your role becomes reviewing and refining rather than writing from scratch—which is often the difference between documentation that gets done and documentation that gets skipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operational Documentation That Actually Helps
&lt;/h2&gt;

&lt;p&gt;One of the most valuable types of documentation is also the most overlooked: information organized for when things go wrong. During incidents, you need answers fast, not comprehensive explanations.&lt;/p&gt;

&lt;p&gt;AI excels at creating focused, actionable documentation because you can specify exactly what situation you're optimizing for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create incident response documentation for this codebase. Focus on: 1) How to quickly identify what component is failing, 2) Common failure modes and their symptoms, 3) Step-by-step debugging workflows, 4) Who to contact for different types of issues. Write this as if the person reading it is stressed, tired, and needs answers in under 5 minutes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This type of documentation serves a completely different purpose than your standard README or API docs. It's designed for when your most knowledgeable developers aren't available and someone needs to resolve an issue quickly.&lt;/p&gt;

&lt;p&gt;The beauty of AI-generated operational docs is that they're naturally structured for scan-ability rather than linear reading—exactly what you need during high-pressure situations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capturing Institutional Knowledge
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyyyq1zrlfquigu4a4mbu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyyyq1zrlfquigu4a4mbu.jpg" alt="Conversation over coffee" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's where AI really shines: helping you identify and document the knowledge that exists only in people's heads. This institutional knowledge is often the difference between a change that takes 30 minutes and one that takes 3 hours of debugging.&lt;/p&gt;

&lt;p&gt;You can surface these knowledge gaps by asking AI to analyze your code from a risk perspective:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Analyze this code and identify areas where domain knowledge or business context would be critical for modification. What would a developer need to know about our business, users, or regulatory requirements to safely change this code? What assumptions about data, timing, or external systems are embedded here?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For inline documentation, you can focus on the business logic and integration points that aren't obvious from the code itself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add inline documentation to this code file without changing any of the code. Focus on documenting business logic, data assumptions, and integration points that wouldn't be obvious to someone unfamiliar with our domain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This process often improves the code itself—explaining your logic to AI sometimes reveals opportunities for clearer naming, better structure, or simplified approaches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fe3anibkivba4pv3hv6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fe3anibkivba4pv3hv6.jpg" alt="Two developers looking at docs together" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Documentation a Team Superpower
&lt;/h2&gt;

&lt;p&gt;The real opportunity here isn't just better individual documentation—it's democratizing the ability to create good documentation across your entire team. Developers who previously avoided writing docs because they didn't know where to start now have a collaborative partner to help structure their thoughts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with high-impact documentation&lt;/strong&gt;: Focus on onboarding guides and operational runbooks first. These provide immediate value and create positive momentum around documentation practices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use AI to improve existing docs&lt;/strong&gt;: You can ask AI to review and improve documentation you already have, suggesting missing information or better organization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make it iterative&lt;/strong&gt;: Documentation doesn't need to be perfect on the first pass. Use AI to create initial drafts that you can refine based on team feedback and real usage patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leverage different formats&lt;/strong&gt;: AI can help create everything from README files to inline comments to architectural decision records, adapting the style and depth based on the audience and purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Tips for Better Results
&lt;/h2&gt;

&lt;p&gt;When working with AI to create documentation, providing context about the intended audience and use case dramatically improves the output. Explain not just what the code does, but who will be using the documentation and in what situations.&lt;/p&gt;

&lt;p&gt;For complex codebases, you might get better results by working with smaller sections and then asking AI to help you organize everything into a coherent structure. Many AI tools can also provide downloadable files if you specify that in your prompt, which saves time on longer documents.&lt;/p&gt;

&lt;p&gt;The goal isn't to replace human judgment in documentation—it's to remove the barriers that prevent good documentation from getting written in the first place. AI handles the initial structure and comprehensive coverage, while you focus on accuracy, team-specific context, and ensuring the documentation actually serves your workflows.&lt;/p&gt;

&lt;p&gt;Good documentation transforms how teams work together. It reduces interruptions, accelerates onboarding, and creates resilience when key team members aren't available. With AI handling the heavy lifting of initial creation, maintaining comprehensive documentation becomes achievable rather than aspirational.&lt;/p&gt;

&lt;p&gt;Your future team members (and your future self during the next production incident) will definitely appreciate the investment.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>documentation</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Choose a Great Tech Hire: Beyond Algorithm Tests and Whiteboard Coding</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Tue, 05 Aug 2025 12:10:00 +0000</pubDate>
      <link>https://dev.to/victoria/how-to-choose-a-great-tech-hire-beyond-algorithm-tests-and-whiteboard-coding-4mk9</link>
      <guid>https://dev.to/victoria/how-to-choose-a-great-tech-hire-beyond-algorithm-tests-and-whiteboard-coding-4mk9</guid>
      <description>&lt;p&gt;I've seen too many hiring processes that focus on the wrong things. Teams spend hours on algorithm puzzles and whiteboard exercises, then hire someone who can't write readable code or collaborate effectively with colleagues. Six months later, they're dealing with either a performance issue or an unexpected resignation from someone who never felt like they fit the team.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1522071820081-009f0129c71c%3Fw%3D1000%26h%3D420%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1522071820081-009f0129c71c%3Fw%3D1000%26h%3D420%26fit%3Dcrop" alt="Team collaboration in tech" width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These candidates don't lack technical ability. The problem is that traditional hiring processes don't predict who will actually succeed and stay on your team. After years of hiring engineers and watching some thrive while others struggle, I've learned that the best predictors of long-term success are often the things most interviews completely miss.&lt;/p&gt;

&lt;p&gt;Here's what I actually look for when hiring engineers, and why these signals matter more than most technical assessments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look for Builders, Not Just Coders
&lt;/h2&gt;

&lt;p&gt;The question that matters most isn't "Can they solve algorithm problems?" It's "Can they build things that solve problems?" There's a fundamental difference between someone who can write code and someone who can deliver working software that serves a purpose.&lt;/p&gt;

&lt;p&gt;When I review candidates, I'm looking for evidence that they've built complete projects from start to finish. Not just coding exercises or tutorial follow-alongs, but actual working software that solves real problems. This could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Command-line utilities&lt;/li&gt;
&lt;li&gt;Web applications&lt;/li&gt;
&lt;li&gt;Automation tools&lt;/li&gt;
&lt;li&gt;Contributions to open source projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complexity matters less than the completeness.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1551650975-87deedd944c3%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1551650975-87deedd944c3%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Developer building software" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I'm really evaluating is their ability to navigate the full software development lifecycle. Can they scope a problem, make technical decisions, handle edge cases, write documentation, and ship something that actually works? These are the skills that translate directly to success on your team, regardless of whether they learned them in a computer science program or taught themselves on weekends.&lt;/p&gt;

&lt;p&gt;The best candidates can walk you through their projects and explain not just how they built something, but why they made specific technical choices. They understand the trade-offs they made and can articulate what they learned from the experience. This kind of thinking is what distinguishes engineers who will contribute meaningfully to your team from those who will struggle to move beyond assigned tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluate Systems Thinking Over Syntax Knowledge
&lt;/h2&gt;

&lt;p&gt;Most technical interviews focus on whether someone knows specific syntax or can solve isolated problems. But the engineers who succeed on teams are the ones who understand how their code fits into larger systems and affects other people's work.&lt;/p&gt;

&lt;p&gt;I look for candidates who demonstrate awareness of follow-on effects. When they describe a project, do they consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance implications?&lt;/li&gt;
&lt;li&gt;Maintainability concerns?&lt;/li&gt;
&lt;li&gt;Impact on other developers or users?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding concepts like mutability, thread safety, and code reusability shows technical competence as well as thinking systematically about software as something that exists in a larger context. Engineers who grasp these concepts naturally write code that's easier to debug, extend, and maintain.&lt;/p&gt;

&lt;p&gt;During interviews, I ask candidates to explain technical trade-offs they've made in their projects. The specific technologies matter less than their ability to reason about complexity, performance, and maintainability. Engineers who think this way will continue learning and adapting as your company's tech stack evolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assess Communication Skills Through Real Examples
&lt;/h2&gt;

&lt;p&gt;Communication skills aren't just a "nice to have" for engineers—they're essential for team effectiveness. But most hiring processes assess communication through artificial interview scenarios rather than looking at how candidates actually communicate about technical topics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1556075798-4825dfaaf498%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1556075798-4825dfaaf498%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Code review and collaboration" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I spend significant time reviewing candidates' written communication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do they explain their projects in README files?&lt;/li&gt;
&lt;li&gt;How do they participate in open source discussions?&lt;/li&gt;
&lt;li&gt;Can they write clear, helpful documentation?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These examples reveal how they'll communicate with your team when explaining technical decisions, documenting systems, or participating in code reviews.&lt;/p&gt;

&lt;p&gt;Pay attention to how candidates describe complex technical concepts during interviews. Can they adjust their explanation based on their audience's technical background? Do they provide context and examples? Can they acknowledge when they don't know something without becoming defensive?&lt;/p&gt;

&lt;p&gt;The engineers who succeed long-term are those who can collaborate effectively across different skill levels and backgrounds. They can explain technical concepts to non-technical stakeholders, provide helpful code review feedback, and contribute to architectural discussions. These collaborative skills are often better predictors of success than pure technical ability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identify Team Players Through Contribution Patterns
&lt;/h2&gt;

&lt;p&gt;The best predictor of how someone will behave on your team is how they've behaved on other teams. Rather than asking hypothetical questions about teamwork, look at concrete examples of how candidates have collaborated with others.&lt;/p&gt;

&lt;p&gt;Open source contributions provide excellent insight into someone's collaborative style:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do they handle feedback on their code?&lt;/li&gt;
&lt;li&gt;Do they contribute thoughtfully to discussions?&lt;/li&gt;
&lt;li&gt;Can they work within existing conventions and standards?&lt;/li&gt;
&lt;li&gt;Do they help other contributors or just focus on their own work?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For candidates without extensive open source history, look at how they talk about past team experiences. Do they credit others for successes? Can they describe situations where they helped colleagues or learned from feedback? How do they handle disagreement or conflict?&lt;/p&gt;

&lt;p&gt;I'm particularly interested in candidates who show evidence of helping others grow. Engineers who mentor junior developers, contribute to team documentation, or improve development processes tend to have a positive impact that extends far beyond their individual contributions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluate Learning Ability Over Current Knowledge
&lt;/h2&gt;

&lt;p&gt;Technology changes rapidly, which means the specific skills someone has today matter less than their ability to acquire new skills as needed. The engineers who thrive long-term are those who stay curious and adapt effectively to new challenges.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1434030216411-0b793f4b4173%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1434030216411-0b793f4b4173%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Continuous learning concept" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During interviews, I ask candidates about times they had to learn something completely new for a project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How did they approach unfamiliar technologies?&lt;/li&gt;
&lt;li&gt;What resources did they use?&lt;/li&gt;
&lt;li&gt;How did they validate their understanding?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The process they describe reveals more about their potential than any specific technology they currently know.&lt;/p&gt;

&lt;p&gt;I also look for evidence of intellectual humility. Can candidates acknowledge the limits of their knowledge? Do they ask thoughtful questions? Are they excited about learning from more experienced team members? Engineers who combine confidence in their abilities with openness to learning tend to grow quickly and integrate well with existing teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Your Hiring Process
&lt;/h2&gt;

&lt;p&gt;Identifying these qualities requires a different approach than traditional technical interviews. Instead of algorithm problems, focus on discussing real projects and technical decisions. Instead of whiteboard coding, review actual code they've written and ask them to explain their thinking.&lt;/p&gt;

&lt;p&gt;Spend time on behavioral questions that reveal collaborative patterns and learning ability. Make time for informal conversation about what kind of work environment they thrive in and what they're excited to learn next.&lt;/p&gt;

&lt;p&gt;Most importantly, involve your team in the hiring process. The people who will work directly with your new hire are often better at assessing team fit than individual interviewers making isolated decisions.&lt;/p&gt;

&lt;p&gt;Remember that hiring is ultimately about predicting future success, not just evaluating current abilities. The candidates who can build complete projects, think systematically about technical decisions, communicate effectively, and continue learning will contribute more to your team's long-term success than those who simply perform well on coding tests.&lt;/p&gt;

&lt;p&gt;Your perfect candidate isn't necessarily the most technically skilled or the most knowledgeable about your domain. It's the person who will grow with your team and contribute to the kind of collaborative, effective engineering culture that retains great people and delivers great software.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What qualities do you prioritize when hiring engineers? Share your experiences in the comments below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hiring</category>
      <category>engineering</category>
      <category>career</category>
      <category>management</category>
    </item>
    <item>
      <title>Do One Thing: Mastering Prioritization for High-Performing Teams</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Tue, 29 Jul 2025 13:03:00 +0000</pubDate>
      <link>https://dev.to/victoria/do-one-thing-mastering-prioritization-for-high-performing-teams-4091</link>
      <guid>https://dev.to/victoria/do-one-thing-mastering-prioritization-for-high-performing-teams-4091</guid>
      <description>&lt;p&gt;In the engineering teams I lead, "priority" has no plural form. This drives some people slightly crazy, especially those who like to hedge their bets with phrases like "top priorities" or "critical priorities." But I've learned that the moment you allow multiple top priorities, you've essentially created zero priorities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1552664730-d307ca884978%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1552664730-d307ca884978%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Team planning session with prioritization board" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I discovered this the hard way while working with a team that was constantly context-switching between "urgent" projects. Everyone was busy, morale was decent, but we weren't actually shipping much of value. During one particularly frustrating week, I counted seventeen different tasks that had been labeled as "high priority" by various stakeholders. Our standups felt like disaster reports, and I realized we'd created a system where being busy had become more important than being effective.&lt;/p&gt;

&lt;p&gt;The solution turned out to be surprisingly simple, though not easy to implement: put everything into a single, ordered list where only one thing can be most important at any given time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Radical Transparency of a Central List
&lt;/h2&gt;

&lt;p&gt;Most teams I've encountered operate like a collection of individual to-do lists with some coordination meetings sprinkled on top. Engineering works on technical debt, product pushes for new features, leadership wants infrastructure improvements, and everyone optimizes their own piece of the puzzle. The result is a lot of activity that doesn't add up to meaningful progress.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1611224923853-80b023f02d71%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1611224923853-80b023f02d71%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Digital task board showing organized priorities" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A single, centralized, prioritized list changes the entire dynamic. Everyone can see what's actually being worked on, what's coming next, and most importantly, what's not getting done and why. This visibility creates natural conversations about trade-offs that simply don't happen when work is siloed.&lt;/p&gt;

&lt;p&gt;I've watched teams discover they were working on competing solutions to the same problem, simply because no one had a complete view of active work. Others realized they were delaying important projects because someone assumed "someone else" was handling the dependency. When everything is visible and ordered, these coordination problems become obvious and fixable.&lt;/p&gt;

&lt;p&gt;The transparency also creates a different kind of accountability. When priorities are public and explicit, it becomes much harder to justify working on pet projects or avoiding difficult tasks. The list becomes a shared source of truth that guides decisions rather than each person interpreting priorities through their own lens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autonomy Within Structure
&lt;/h2&gt;

&lt;p&gt;One concern I hear frequently is that a single priority list will turn people into order-takers rather than creative problem-solvers. In practice, I've found exactly the opposite happens when you implement it correctly.&lt;/p&gt;

&lt;p&gt;The key is encouraging people to choose the highest-priority task they can effectively tackle rather than assigning specific tasks to specific people. Someone might skip over the absolute top item because it requires domain knowledge they don't have, but they can pick up the second or third item that lets them contribute meaningfully while learning something new.&lt;/p&gt;

&lt;p&gt;This approach leverages the fact that your team members understand their own capabilities and growth goals better than you do. A senior engineer might choose to mentor a junior developer on a complex task. A frontend specialist might want to tackle a backend task to broaden their skills. These decisions create better outcomes in the long term than top-down task assignment while still maintaining focus on organizational priorities.&lt;/p&gt;

&lt;p&gt;The autonomy comes from trusting people to make good decisions about how to contribute most effectively, while the structure comes from ensuring those contributions align with actual business needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Art of Making Yourself Redundant
&lt;/h2&gt;

&lt;p&gt;If your team frequently asks you what they should work on next, you've accidentally created a bottleneck—and it's you. This is one of the most common scaling problems I see with engineering leaders who transition from individual contributor roles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1600880292203-757bb62b4baf%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1600880292203-757bb62b4baf%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Team collaboration and documentation" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal is building a system where intelligent people can make good decisions without constant input from leadership. This requires making context painfully available—team goals, product strategy, architectural decisions, customer feedback, and anything else that influences prioritization should be accessible and current.&lt;/p&gt;

&lt;p&gt;I've found that the difference between teams that scale smoothly and teams that hit velocity walls usually comes down to how well they've documented the reasoning behind decisions. When someone can understand not just what to build but why it matters and how it fits into the larger strategy, they can make smart trade-offs independently.&lt;/p&gt;

&lt;p&gt;This redundancy becomes especially critical during high-pressure situations. When systems are down or deadlines are looming, you don't want your team waiting for permission to take action. Teams that have practiced autonomous decision-making within clear constraints can respond quickly and effectively without requiring heroic coordination efforts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cultural Transformation
&lt;/h2&gt;

&lt;p&gt;What surprises most leaders is how much this simple change affects team culture. When priorities are clear and transparent, several things happen that go far beyond improved task management.&lt;/p&gt;

&lt;p&gt;First, political conversations about priority disappear. There's no point in lobbying for your favorite project when the criteria for prioritization are explicit and the current order is visible to everyone. Energy that was spent on organizational maneuvering gets redirected toward actual work.&lt;/p&gt;

&lt;p&gt;Second, people start thinking about their contributions differently. Instead of optimizing for individual productivity, they begin considering how their work fits into team objectives. This naturally leads to better collaboration and knowledge sharing.&lt;/p&gt;

&lt;p&gt;Third, the team develops a shared sense of progress and momentum. When everyone can see important work getting completed in priority order, it creates a satisfying rhythm that isolated individual achievements can't match.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Reality
&lt;/h2&gt;

&lt;p&gt;The biggest challenge isn't creating the list—it's maintaining the discipline to use it consistently. Teams often start strong but gradually drift back to multiple priority tracks when pressure increases or when compelling new opportunities arise.&lt;/p&gt;

&lt;p&gt;I've learned to treat priority discipline like any other technical practice that requires ongoing attention. Schedule regular review sessions to reorder the list, have explicit discussions about what we're choosing not to do, and consistently communicate why keeping a single-priority focus helps maintain development velocity.&lt;/p&gt;

&lt;p&gt;The payoff: teams that ship more valuable work with less stress and confusion. When everyone understands what matters most and feels empowered to contribute effectively, both productivity and job satisfaction improve dramatically.&lt;/p&gt;

&lt;p&gt;Most importantly, single-priority focus creates sustainable high performance rather than the boom-and-bust cycles that come from constantly shifting between competing urgent demands. Teams learn to work steadily toward important goals rather than reacting to whatever feels most pressing in the moment.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your experience with team prioritization? Have you found effective ways to maintain focus while preserving autonomy? Share your thoughts in the comments below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>productivity</category>
      <category>teamwork</category>
      <category>management</category>
    </item>
    <item>
      <title>The Descent Is Harder Than the Climb: Lessons in Leadership from Mt. Fuji</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Wed, 23 Jul 2025 18:24:48 +0000</pubDate>
      <link>https://dev.to/victoria/the-descent-is-harder-than-the-climb-lessons-in-leadership-from-mt-fuji-3fbo</link>
      <guid>https://dev.to/victoria/the-descent-is-harder-than-the-climb-lessons-in-leadership-from-mt-fuji-3fbo</guid>
      <description>&lt;p&gt;In 2017, I climbed Mt. Fuji in sneakers. This was not a deliberate choice to increase the challenge—it was the result of excellent research and poor judgment about what that research actually meant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1490806843957-31f4c9a91c65%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1490806843957-31f4c9a91c65%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Mt. Fuji sunrise view" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything I'd read suggested that Mt. Fuji was the "cakewalk of mountain climbing." Physically, the hardest portions amounted to scrambling over some big boulders. Most of the climb was no more taxing than hiking or climbing stairs. Japanese folks in their eighties made the journey for spiritual reasons. There were huts along the way for rest, food, and water. Based on this research, I concluded that sneakers would be perfectly adequate.&lt;/p&gt;

&lt;p&gt;The ascent was everything I'd been promised. I experienced sights I'd never imagined—cities glowing through breaks in clouds from above, walking through paths of grey nothingness where the trail disappeared into cloud cover. Each station marker brought genuine pride and accomplishment. Even the pre-dawn summit queue with 5,000 other climbers, standing in freezing darkness for hours, felt manageable. We reached the summit before sunrise, and it remains one of the most beautiful moments I've experienced.&lt;/p&gt;

&lt;p&gt;Then came the descent. That's where I learned that all the research in the world about reaching goals doesn't prepare you for what comes after you achieve them.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Success Becomes the Set Up for Failure
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1551632811-561732d1e306%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1551632811-561732d1e306%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Rocky mountain descent path" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The descent from Mt. Fuji is essentially a loosely-packed dirt and gravel road on a steep decline. With proper hiking boots and trekking poles, it's probably manageable. In flat-soled street shoes, I fell constantly, and fell hard—every three steps, for hours. I tried to take larger steps; it didn't help. I tried to take smaller steps; that didn't help, either. I tried cunningly to find a way to surf-slide my way down the mountainside and nearly ended up with a mouthful of dirt. As if literally rubbing salt into my wounds, without the gaiters I hadn't brought, sand found its way into my shoes. It was without a doubt the most stupefyingly discouraging experience of my life.&lt;/p&gt;

&lt;p&gt;As I picked myself up repeatedly, covered in dirt with scratched elbows, seasoned hikers passed me with ease. Many of them could have been my grandparents, using proper equipment and technique to descend at a steady pace while I struggled and stopped to pour tiny rocks out of my sneakers. The contrast was humbling and instructive.&lt;/p&gt;

&lt;p&gt;This experience taught me something crucial about leadership that I've applied countless times since: &lt;strong&gt;the skills and preparation that get you to success are often different from the skills required to maintain or scale that success.&lt;/strong&gt; The descent is frequently harder than the climb, and most people don't prepare for it adequately.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Post-Achievement Challenge
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1522202176988-66273c2fd55f%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1522202176988-66273c2fd55f%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Team meeting in modern office" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In business and team leadership, I've watched this pattern repeat consistently. The energy, skills, and resources required to achieve a goal are usually well-understood and planned for. But the challenges that come after success—maintaining market position, scaling team culture, or managing the operational complexity of growth—often catch leaders unprepared.&lt;/p&gt;

&lt;p&gt;I've seen teams that executed brilliant product launches struggle with customer support and maintenance. Startups that successfully raised funding stumble when it comes to executing on their promises to investors. Engineering teams that built innovative solutions fail to create sustainable systems for maintaining and scaling those solutions.&lt;/p&gt;

&lt;p&gt;The problem isn't lack of capability—it's that the descent requires different preparation and different skills than the ascent. What gets you to the summit (innovation, speed, breakthrough thinking) often isn't what gets you safely back to basecamp (consistency, processes, systematic execution).&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning from Those Who've Made the Journey
&lt;/h2&gt;

&lt;p&gt;Watching those experienced hikers pass me on Mt. Fuji was initially frustrating, but it became one of the most valuable parts of the experience. They had proper equipment, understood the terrain, and moved with confidence that came from experience. Most importantly, they had prepared specifically for the descent, not just the climb.&lt;/p&gt;

&lt;p&gt;In leadership roles, I've learned to actively seek out people who've successfully navigated the "descent" phase of challenges I'm facing. Entrepreneurs who've managed hypergrowth. Product managers who've maintained market leadership over multiple years. Engineering leaders who've scaled teams from ten to fifty people, or CEOs who've scaled companies from fifty to five hundred.&lt;/p&gt;

&lt;p&gt;These conversations can reveal patterns you may not have discovered on your own. Successful scaling requires different organizational structures than startup growth. Maintaining team culture during rapid hiring requires intentional systems that don't emerge naturally. Sustaining innovation while managing operational complexity demands new kinds of leadership skills.&lt;/p&gt;

&lt;p&gt;People who've successfully managed the descent often have hard-won wisdom about preparation and technique that isn't captured in most "how to reach the summit" advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Skills Before You Need Them
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1484480974693-6ca0a78fb36b%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1484480974693-6ca0a78fb36b%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Person planning with notebooks and laptop" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most effective leaders I know prepare for post-success challenges while they're still climbing toward their initial goals. They think systematically about what will be required to maintain and scale whatever they're building, not just achieve it.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building operational capabilities alongside product capabilities&lt;/li&gt;
&lt;li&gt;Developing team management skills in individual contributors&lt;/li&gt;
&lt;li&gt;Creating sustainable processes while you're still in startup mode&lt;/li&gt;
&lt;li&gt;Planning for the maintenance and evolution of systems as part of their initial implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also means recognizing that the mindset and skills that drive breakthrough achievements—risk-taking, speed, creative problem-solving—need to be balanced with different capabilities like consistency, systematic thinking, and process optimization.&lt;/p&gt;

&lt;p&gt;I've learned to explicitly ask: &lt;strong&gt;"What will success look like, and what challenges will that create?"&lt;/strong&gt; This question reveals preparation gaps that aren't obvious when you're focused entirely on reaching your goals.&lt;/p&gt;

&lt;h2&gt;
  
  
  When You Find Yourself Unprepared
&lt;/h2&gt;

&lt;p&gt;Despite best intentions, you'll sometimes find yourself in descent mode without proper preparation—leading a team through unexpected growth, managing a product that succeeded beyond projections, or scaling systems that weren't designed for current loads. The Mt. Fuji experience taught me how to navigate these situations effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, acknowledge the reality of your situation without wasting energy on regret about preparation gaps. You can't change what you didn't know or plan for previously, but you can adapt your approach based on current conditions. Take the time to solidify new goals in writing, then evaluate whether your efforts are serving them effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second&lt;/strong&gt;, focus on learning from people who are managing similar challenges successfully. This isn't the time for pride or trying to figure everything out independently. The hikers who passed me weren't showing off—they had practical knowledge that could help. Conversations you have with others who came before you can save you from a lot of stumbles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third&lt;/strong&gt;, lift your gaze. While the ascent phase requires day-to-day tactical thinking, the descent phase requires a strategic longer-term outlook. Implementing systems and culture that support continued success will require patience, persistence, and often a completely different pace than what got you to the summit. Expecting it to be as expedient as the climb leads to frustration and poor decision-making.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding Meaning in the Difficult Parts
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1569718212165-3a8278d5f624%3Fw%3D800%26h%3D400%26fit%3Dcrop" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1569718212165-3a8278d5f624%3Fw%3D800%26h%3D400%26fit%3Dcrop" alt="Bowl of ramen" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eventually, I reached the bottom of Mt. Fuji, exhausted and humbled but intact. At a tiny basecamp shop, I ate the most delicious bowl of ramen and the tastiest mountain-shaped sponge cake I'll likely ever have.&lt;/p&gt;

&lt;p&gt;Even when you're unprepared and struggling, there's value in the journey itself. The descent taught me lessons about preparation, humility, and persistence that I've applied to all sorts of challenges for years since.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing for Your Next Descent
&lt;/h2&gt;

&lt;p&gt;There is a Japanese proverb: "A wise man will climb Mt Fuji once; a fool will climb Mt Fuji twice." I suspect this wisdom is based entirely on the difficulty of the descent. But in leadership, you don't get to choose how many times you'll face descent challenges—they're inevitable parts of any significant journey.&lt;/p&gt;

&lt;p&gt;The key is recognizing that achieving your goals is often just the beginning of a different kind of challenge. Success creates new problems that require different skills, different preparation, and different mindsets than what got you there initially.&lt;/p&gt;

&lt;p&gt;Whether you're building teams, scaling products, or managing organizational growth, prepare for the descent while you're planning the climb. Study what happens after success. Learn from people who've navigated similar transitions. Build operational capabilities alongside innovative ones.&lt;/p&gt;

&lt;p&gt;Most importantly, remember that the descent is still part of the journey, not a failure of the ascent. The challenges that come with success are signs that you've accomplished something meaningful. Navigate them with patience, preparation, and the understanding that getting back to basecamp safely can be an even more important achievement than reaching the summit.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>career</category>
      <category>management</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Future-Proof Your Software Engineering Career for the Age of AGI</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Wed, 21 Aug 2024 13:30:24 +0000</pubDate>
      <link>https://dev.to/victoria/how-to-future-proof-your-software-engineering-career-for-the-age-of-agi-1726</link>
      <guid>https://dev.to/victoria/how-to-future-proof-your-software-engineering-career-for-the-age-of-agi-1726</guid>
      <description>&lt;p&gt;In the viral essay &lt;em&gt;The Decade Ahead&lt;/em&gt;, Leopold Aschenbrenner predicts that Artificial General Intelligence (AGI) will be a reality in only a few years.&lt;/p&gt;

&lt;p&gt;If that happens, the skills and experiences that will prepare you for software engineering with AGI will likely focus on understanding, managing, and integrating highly autonomous systems.&lt;/p&gt;

&lt;p&gt;Here are some key areas of work to focus on now to prepare you for the AGI era.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Mastering Machine Learning and Deep Learning
&lt;/h2&gt;

&lt;p&gt;Engineers with expertise in these fields will be at the forefront. You’ll need to understand how to create and train models that can learn from a wide variety of data sources, adapt to new information, and generalize across different tasks. Focus on areas like reinforcement learning, unsupervised learning, and neural networks. Keep an eye out for new paradigms in AI that might emerge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : Machine Learning Engineer, AI Research Scientist.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Software Engineering with a Focus on AI Integration
&lt;/h2&gt;

&lt;p&gt;Traditional software engineering roles will evolve to integrate AI components seamlessly. This includes developing frameworks where AGI can be applied or integrated into existing systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : Full Stack Developer with AI specialization, AI Software Engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Navigating Ethics and AI Governance
&lt;/h2&gt;

&lt;p&gt;As AGI could pose significant ethical and governance challenges, roles focusing on the ethical implications, policy-making, and regulatory compliance will be crucial. This includes ensuring AGI systems operate within legal and ethical frameworks. Public as well as private sector experience will be valuable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : AI Ethics Analyst, Policy Advisor for AI, Compliance Officer for AI Systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Evolving Human Computer Interaction (HCI)
&lt;/h2&gt;

&lt;p&gt;HCI will quickly transform into Human-AI Interaction Design. AGI systems will need to interact seamlessly with humans. Companies will need interfaces where humans can interact with AGI systems effectively, built by engineers who understand cognitive psychology and UX/UI design for AI systems.&lt;/p&gt;

&lt;p&gt;Engineers skilled in designing intuitive interfaces and interactions between humans and intelligent systems will be highly successful in AGI integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : Interaction Designer for AI, User Experience Researcher for AI Systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Enhancing Autonomous Systems and Robotics
&lt;/h2&gt;

&lt;p&gt;If AGI leads to more autonomous robots, engineers who can design, build, and program robots with AGI capabilities will be in demand. This includes understanding not just robotics but how AGI can enhance robotic functionality.&lt;/p&gt;

&lt;p&gt;Working on autonomous systems, whether in robotics, self-driving vehicles, or drones, can provide practical experience with highly independent systems. These skills will be transferrable to managing and optimizing AGI-based autonomous agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : Robotics Engineer, Automation Specialist.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Pioneering Hardware Development for AGI
&lt;/h2&gt;

&lt;p&gt;We’ll need engineers working on specialized hardware that can support AGI. Technologies like neuromorphic computing chips or quantum computing might be necessary for the computational power AGI would require.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : Hardware Engineer for AI, Quantum Computing Engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Securing the Future: Cybersecurity for AGI
&lt;/h2&gt;

&lt;p&gt;AGI systems will introduce new security challenges. Engineers with expertise in cybersecurity will be in high demand to protect AGI systems from national security threats, ensure data privacy, and secure AI-driven decision-making processes against manipulation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : AI Security Specialist, Cybersecurity Analyst for AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Data Engineering: Fueling AGI with Information
&lt;/h2&gt;

&lt;p&gt;Handling large-scale data systems will remain important. Engineers with expertise in big data, data pipelines, and real-time data processing will be essential in feeding AGI systems the information they need to operate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : Data Engineer, Big Data Architect.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Building Infrastructure for AGI
&lt;/h2&gt;

&lt;p&gt;AGI will require robust and scalable infrastructure on a never-before-seen scale. Engineers with experience in cloud computing, distributed systems, and infrastructure as code (IaC) will be crucial in building the systems that support AGI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : Cloud Infrastructure Engineer, Systems Architect for AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Cross-Disciplinary Collaboration in the AGI Era
&lt;/h2&gt;

&lt;p&gt;Working in roles that involve cross-disciplinary collaboration, such as roles in research or innovation labs, can provide engineers with the ability to think broadly and integrate knowledge from various fields. Combining skills from fields such as biology (for bioinformatics or synthetic biology with AGI) and psychology (for understanding human-AI interaction) will be vital in the AGI era.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : Bioinformatics Engineer, Environmental Data Scientist.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Education and Training for an AGI-Ready Workforce
&lt;/h2&gt;

&lt;p&gt;Developing new educational programs or training modules that teach engineers how to work with AGI, focusing on both technical skills and the philosophical or ethical considerations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : AI Curriculum Developer, Training Specialist for AI Technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Shaping Regulations in an AGI-Driven World
&lt;/h2&gt;

&lt;p&gt;Engineers working on regulatory technology (RegTech) will gain insight into compliance and governance, which will be critical as AGI evolves within legal frameworks. Understanding how to navigate and shape regulations will be vital.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : Regulatory Engineer, Compliance Specialist for AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Research and Development (R&amp;amp;D) in AGI-related Areas
&lt;/h2&gt;

&lt;p&gt;Finally, engineers who are involved in cutting-edge research in AGI, cognitive computing, or advanced AI labs will be directly contributing to and understanding the frontiers of AGI technology, giving them a head start in a world where AGI is a reality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job titles to watch&lt;/strong&gt; : AGI Research Scientist, Cognitive Computing Engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Job Titles
&lt;/h2&gt;

&lt;p&gt;The transition to an AGI world would likely see a blend of these roles, where engineers might need to be polymaths, understanding not just one but multiple areas of technology and science. It’s important to grow your technical skills, but also practice adaptability and continuous learning.&lt;/p&gt;

&lt;p&gt;Be on the lookout for roles that might not directly mention AGI but are foundational in AI, machine learning, and related technologies. As you choose your next role, think ahead to how you can tailor your focus and future-proof your work for the AGI era.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Post to your static website from your iPhone</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Sun, 05 May 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/victoria/post-to-your-static-website-from-your-iphone-d2m</link>
      <guid>https://dev.to/victoria/post-to-your-static-website-from-your-iphone-d2m</guid>
      <description>&lt;p&gt;I love websites. I love static sites in particular. But I know that sometimes it’s just not practical to write and post only from your computer. With my hands full raising a family, I do a lot more development in stops and starts from my phone these days than I thought I ever would.&lt;/p&gt;

&lt;p&gt;So I brought together everything that’s great about Hugo plus everything that’s great about sharing your 3AM thoughts with the world from your phone, thanks to Collected Notes. I put it in a new Hugo site template with a fancy new theme I call Quint.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://github.com/victoriadrake/quint-demo" rel="noopener noreferrer"&gt;deploy the Quint site template with one button&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Quint template can use the Collected Notes app as a CMS and also saves your posts to the site repository, for &lt;a href="https://victoria.dev/blog/digital-resilience-redundancy-for-websites-and-communications/" rel="noopener noreferrer"&gt;redundancy&lt;/a&gt;. It fetches new posts each time you build, and if you’re deploying via Netlify or GitHub Actions, you can use a webhook to deploy the site whenever you make a new post with Collected Notes.&lt;/p&gt;

&lt;p&gt;To set up your own site:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy the Quint template to Netlify with the button above, or clone the repo if you plan to use another deployment solution.&lt;/li&gt;
&lt;li&gt;Sign up for &lt;a href="https://collectednotes.com/" rel="noopener noreferrer"&gt;Collected Notes&lt;/a&gt; if you haven’t already (there’s a free plan) and download the Collected Notes app on your iPhone.&lt;/li&gt;
&lt;li&gt;Update the &lt;code&gt;utils/fetch-posts.js&lt;/code&gt; file to use your Collected Notes site name.&lt;/li&gt;
&lt;li&gt;Allow the GitHub Action to push changes back to your repository to save your posts. Under Settings &amp;gt; Actions &amp;gt; General &amp;gt; Workflow permissions, choose Read and write permissions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Netlify will trigger a new build each time you push to your site repo, or, if you have a Collected Notes Premium subscription, you can set a &lt;a href="https://docs.netlify.com/configure-builds/build-hooks/" rel="noopener noreferrer"&gt;Netlify Build Hook&lt;/a&gt; URL in your Collected Notes site settings to automatically redeploy the site when you make a post or update an existing post.&lt;/p&gt;

&lt;p&gt;I hope this template helps out busy people like you! I’m using this solution myself, of course, to write the &lt;a href="https://lightlylived.com/" rel="noopener noreferrer"&gt;next chapter of my one-bag era&lt;/a&gt; – with my phone in one hand and a coffee in the other.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to send long text input to ChatGPT using the OpenAI API</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Tue, 26 Sep 2023 09:46:36 +0000</pubDate>
      <link>https://dev.to/victoria/how-to-send-long-text-input-to-chatgpt-using-the-openai-api-119</link>
      <guid>https://dev.to/victoria/how-to-send-long-text-input-to-chatgpt-using-the-openai-api-119</guid>
      <description>&lt;p&gt;In a &lt;a href="https://dev.to/victoria/optimizing-text-for-chatgpt-nlp-and-text-pre-processing-techniques-5cop"&gt;previous post&lt;/a&gt;, I showed how you can apply text preprocessing techniques to shorten your input length for ChatGPT. Today in the web interface (&lt;a href="https://chat.openai.com/" rel="noopener noreferrer"&gt;chat.openai.com&lt;/a&gt;), ChatGPT allows you to send a message with a maximum token length of 4,096.&lt;/p&gt;

&lt;p&gt;There are bound to be situations in which this isn’t enough, such as when you want to read in a large amount of text from a file. Using the OpenAI API allows you to send many more tokens in a messages array, with the maximum number depending on your chosen model. This lets you provide large amounts of text to ChatGPT using chunking. Here’s how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chunking your input
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;gpt-4&lt;/code&gt; model currently has a maximum content length token limit of 8,192 tokens. (&lt;a href="https://platform.openai.com/docs/models" rel="noopener noreferrer"&gt;Here are the docs containing current limits for all the models&lt;/a&gt;.) Remember that you can first apply text preprocessing techniques to reduce your input size – in my &lt;a href="https://dev.to/blog/optimizing-text-for-chatgpt-nlp-and-text-pre-processing-techniques/"&gt;previous post&lt;/a&gt; I achieved a 28% size reduction without losing meaning with just a little tokenization and pruning.&lt;/p&gt;

&lt;p&gt;When this isn’t enough to fit your message within the maximum message token limit, you can take a general programmatic approach that sends your input in message chunks. The goal is to divide your text into sections that each fit within the model’s token limit. The general idea is to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tokenize and split text into chunks&lt;/strong&gt; based on the model’s token limit. It’s better to keep message chunks slightly below the token limit since the token limit is shared between your message and ChatGPT’s response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintain context&lt;/strong&gt; between chunks, e.g. avoid splitting a sentence in the middle.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each chunk is sent as a separate message in the conversation thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling responses
&lt;/h2&gt;

&lt;p&gt;You send your chunks to ChatGPT using the OpenAI library’s &lt;code&gt;ChatCompletion&lt;/code&gt;. ChatGPT returns individual responses for each message, so you may want to process these by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Concatenating responses&lt;/strong&gt; in the order you sent them to get a coherent answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manage conversation flow&lt;/strong&gt; by keeping track of which response refers to which chunk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Formatting the response&lt;/strong&gt; to suit your desired output, e.g. replacing &lt;code&gt;\n&lt;/code&gt; with line breaks.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Using the OpenAI API, you can send multiple messages to ChatGPT and ask it to wait for you to provide all of the data before answering your prompt. Being a language model, you can provide these instructions to ChatGPT in plain language. Here’s a suggested script:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prompt: Summarize the following text for me&lt;/p&gt;

&lt;p&gt;To provide the context for the above prompt, I will send you text in parts. When I am finished, I will tell you “ALL PARTS SENT”. Do not answer until you have received all the parts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I created &lt;a href="https://github.com/victoriadrake/chatgptmax" rel="noopener noreferrer"&gt;a Python module, &lt;code&gt;chatgptmax&lt;/code&gt;&lt;/a&gt;, that puts all this together. It breaks up a large amount of text by a given maximum token length and sends it in chunks to ChatGPT.&lt;/p&gt;

&lt;p&gt;You can install it with &lt;code&gt;pip install chatgptmax&lt;/code&gt;, but here’s the juicy part:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tiktoken&lt;/span&gt;

&lt;span class="c1"&gt;# Set up your OpenAI API key
# Load your API key from an environment variable or secret management service
&lt;/span&gt;&lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;text_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chat_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model_token_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Send the prompt at the start of the conversation and then send chunks of text_data to ChatGPT via the OpenAI API.
    If the text_data is too long, it splits it into chunks and sends each chunk separately.

    Args:
    - prompt (str, optional): The prompt to guide the model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s response.
    - text_data (str, optional): Additional text data to be included.
    - max_tokens (int, optional): Maximum tokens for each API call. Default is 2500.

    Returns:
    - list or str: A list of model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s responses for each chunk or an error message.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# Check if the necessary arguments are provided
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: Prompt is missing. Please provide a prompt.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;text_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: Text data is missing. Please provide some text data.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Initialize the tokenizer
&lt;/span&gt;    &lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tiktoken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encoding_for_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chat_model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Encode the text_data into token integers
&lt;/span&gt;    &lt;span class="n"&gt;token_integers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Split the token integers into chunks based on max_tokens
&lt;/span&gt;    &lt;span class="n"&gt;chunk_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;token_integers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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;chunk_size&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="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;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_integers&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Decode token chunks back to strings
&lt;/span&gt;    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&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;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;To provide the context for the above prompt, I will send you text in parts. When I am finished, I will tell you &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ALL PARTS SENT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;. Do not answer until you have received all the parts.&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="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="c1"&gt;# Check if total tokens exceed the model's limit and remove oldest chunks if necessary
&lt;/span&gt;        &lt;span class="nf"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&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;msg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;model_token_limit&lt;/span&gt;
        &lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Remove the oldest chunk
&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chat_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;chatgpt_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatgpt_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Add the final "ALL PARTS SENT" message
&lt;/span&gt;    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ALL PARTS SENT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chat_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;final_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final_response&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;responses&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s an example of how you can use this module with text data read from a file. (&lt;code&gt;chatgptmax&lt;/code&gt; also provides a &lt;a href="https://github.com/victoriadrake/chatgptmax/blob/4431af468435cd51d07779c6d721c8e0016d6bd6/chatgptmax.py#L68" rel="noopener noreferrer"&gt;convenience method&lt;/a&gt; for getting text from a file.)&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;# First, import the necessary modules and the function
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chatgptmax&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;

&lt;span class="c1"&gt;# Define a function to read the content of a file
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_file_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Use the function
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; __main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Specify the path to your file
&lt;/span&gt;    &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path_to_your_file.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Read the content of the file
&lt;/span&gt;    &lt;span class="n"&gt;file_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_file_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Define your prompt
&lt;/span&gt;    &lt;span class="n"&gt;prompt_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the following text for me:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Send the file content to ChatGPT
&lt;/span&gt;    &lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;file_content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Print the responses
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error handling
&lt;/h3&gt;

&lt;p&gt;While the module is designed to handle most standard use cases, there are potential pitfalls to be aware of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Incomplete sentences&lt;/strong&gt; : If a chunk ends in the middle of a sentence, it might alter the meaning or context. To mitigate this, consider ensuring that chunks end at full stops or natural breaks in the text. You could do this by separating the text-chunking task into a separate function that:

&lt;ol&gt;
&lt;li&gt;Splits the text into sentences.&lt;/li&gt;
&lt;li&gt;Iterates over the sentences and adds them to a chunk until the chunk reaches the maximum size.&lt;/li&gt;
&lt;li&gt;Starts a new chunk when the current chunk reaches the maximum size or when adding another sentence would exceed the maximum size.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API connectivity issues&lt;/strong&gt; : There’s always a possibility of timeouts or connectivity problems during API calls. If this is a significant issue for your application, you can include retry logic in your code. If an API call fails, the script could wait for a few seconds and then try again, ensuring that all chunks are processed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limits&lt;/strong&gt; : Be mindful of &lt;a href="https://platform.openai.com/docs/guides/rate-limits/overview" rel="noopener noreferrer"&gt;OpenAI API’s rate limits&lt;/a&gt;. If you’re sending many chunks in rapid succession, you might hit these limits. Introducing a slight delay between calls or spreading out requests can help avoid this.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optimization
&lt;/h3&gt;

&lt;p&gt;As with any process, there’s always room for improvement. Here are a couple of ways you might optimize the module’s chunking and sending process further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parallelizing API calls&lt;/strong&gt; : If &lt;a href="https://platform.openai.com/docs/guides/rate-limits/overview" rel="noopener noreferrer"&gt;OpenAI API’s rate limits&lt;/a&gt; and your infrastructure allow, you could send multiple chunks simultaneously. This parallel processing can speed up the overall time it takes to get responses for all chunks. Unless you have access to OpenAI’s &lt;code&gt;32k&lt;/code&gt; models or need to use small chunk sizes, however, parallelism gains are likely to be minimal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching mechanisms&lt;/strong&gt; : If you find yourself sending the same or similar chunks frequently, consider implementing a caching system. By storing ChatGPT’s responses for specific chunks, you can retrieve them instantly from the cache the next time, saving both time and API calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Now what
&lt;/h2&gt;

&lt;p&gt;If you found your way here via search, you probably already have a use case in mind. Here are some other (startup) ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You’re a researcher&lt;/strong&gt; who wants to save time by getting short summaries of many lengthy articles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You’re a legal professional&lt;/strong&gt; who wants to analyze long contracts by extracting key points or clauses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You’re a financial analyst&lt;/strong&gt; who wants to pull a quick overview of trends from a long report.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You’re a writer&lt;/strong&gt; who wants feedback on a new article or chapter… without having to actually show it to anyone yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do you have a use case I didn’t list? Let me know about it! In the meantime, have fun sending lots of text to ChatGPT with &lt;a href="https://github.com/victoriadrake/chatgptmax" rel="noopener noreferrer"&gt;&lt;code&gt;chatgptmax&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>showdev</category>
      <category>python</category>
    </item>
    <item>
      <title>Optimizing text for ChatGPT: NLP and text pre-processing techniques</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Tue, 19 Sep 2023 16:06:36 +0000</pubDate>
      <link>https://dev.to/victoria/optimizing-text-for-chatgpt-nlp-and-text-pre-processing-techniques-5cop</link>
      <guid>https://dev.to/victoria/optimizing-text-for-chatgpt-nlp-and-text-pre-processing-techniques-5cop</guid>
      <description>&lt;p&gt;In order for chatbots and voice assistants to be helpful, they need to be able to take in and understand our instructions in plain language using Natural Language Processing (NLP). ChatGPT relies on a blend of advanced algorithms and text preprocessing methods to make sense of our words. But just throwing a wall of text at it can be inefficient – you might be dumping in a lot of noise with that signal and hitting the text input limit.&lt;/p&gt;

&lt;p&gt;Text preprocessing can help shorten and refine your input, ensuring that ChatGPT can grasp the essence without getting overwhelmed. In this article, we’ll explore these techniques, understand their importance, and see how they make your interactions with tools like ChatGPT more reliable and productive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text preprocessing
&lt;/h2&gt;

&lt;p&gt;Text preprocessing prepares raw text data for analysis by NLP models. Generally, it distills everyday text (like full sentences) to make it more manageable or concise and meaningful. Techniques include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tokenization:&lt;/strong&gt; splitting up text by sentences or paragraphs. For example, you could break down a lengthy legal document into individual clauses or sentences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extractive summarization:&lt;/strong&gt; selecting key sentences from the text and discarding the rest. Instead of reading an entire 10-page document, extractive summarization could pinpoint the most crucial sentences and give you a concise overview without delving into the details.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abstractive summarization:&lt;/strong&gt; generating a concise representation of the text content, for example, turning a 10-page document into a brief paragraph that captures the document’s essence in new wording.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pruning:&lt;/strong&gt; removing redundant or less relevant parts. For example, in a verbose email thread, pruning can help remove all the greetings, sign-offs, and other repetitive elements, leaving only the core content for analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While all these techniques can help reduce the size of raw text data, some of these techniques are easier to apply to general use cases than others. Let’s examine how text preprocessing can help us send a large amount of text to ChatGPT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tokenization and ChatGPT input limits
&lt;/h2&gt;

&lt;p&gt;In the realm of Natural Language Processing (NLP), a token is the basic unit of text that a system reads. At its simplest, you can think of a token as a word, but depending on the language and the specific tokenization method used, a token can represent a word, part of a word, or even multiple words.&lt;/p&gt;

&lt;p&gt;While in English we often equate tokens with words, in NLP, the concept is broader. A token can be as short as a single character or as long as a word. For example, with word tokenization, the sentence “Unicode characters such as emojis are not indivisible. ✂️” can be broken down into tokens like this: [“Unicode”, “characters”, “such”, “as”, “emojis”, “are”, “not”, “indivisible”, “.”, “✂️”]&lt;/p&gt;

&lt;p&gt;In another form called Byte-Pair Encoding (BPE), the same sentence is tokenized as: [“Un”, “ic”, “ode”, " characters", " such", " as", " em, “oj”, “is”, " are", " not", " ind", “iv”, “isible”, “.”, " �", “�️”]. The emoji itself is split into tokens containing its underlying bytes.&lt;/p&gt;

&lt;p&gt;Depending on the ChatGPT model chosen, your text input size is restricted by tokens. &lt;a href="https://platform.openai.com/docs/models" rel="noopener noreferrer"&gt;Here are the docs containing current limits&lt;/a&gt;. BPE is used by ChatGPT to determine token count, and we’ll discuss it more thoroughly later. First, we can programmatically apply some preprocessing techniques to reduce our text input size and use fewer tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  A general programmatic approach
&lt;/h2&gt;

&lt;p&gt;For a general approach that can be applied programmatically, pruning is a suitable preprocessing technique. One form is &lt;strong&gt;stop word removal,&lt;/strong&gt; or removing common words that might not add significant meaning in certain contexts. For example, consider the sentence:&lt;/p&gt;

&lt;p&gt;“I always enjoy having pizza with my friends on weekends.”&lt;/p&gt;

&lt;p&gt;Stop words are often words that don’t carry significant meaning on their own in a given context. In this sentence, words like “I”, “always”, “enjoy”, “having”, “with”, “my”, “on” are considered stop words.&lt;/p&gt;

&lt;p&gt;After removing the stop words, the sentence becomes:&lt;/p&gt;

&lt;p&gt;“pizza friends weekends.”&lt;/p&gt;

&lt;p&gt;Now, the sentence is distilled to its key components, highlighting the main subject (pizza) and the associated context (friends and weekends). If you find yourself wishing you could convince people to do this in real life (&lt;em&gt;cough_meetings_cough&lt;/em&gt;)… you aren’t alone.&lt;/p&gt;

&lt;p&gt;Stop word removal is straightforward to apply programmatically: given a list of stop words, examine some text input to see if it contains any of the stop words on your list. If it does, remove them, then return the altered text.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_stopwords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;stopwords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;an&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;but&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;how&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;on&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;what&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;will&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;clean_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stopwords&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clean_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see how effective stop word removal can be, I took the entire text of my &lt;a href="https://techleaderdocs.com" rel="noopener noreferrer"&gt;Tech Leader Docs newsletter&lt;/a&gt; (17,230 words consisting of 104,892 characters) and processed it using the above function. How effective was it? The resulting text contained 89,337 characters, which is about a 15% reduction in size.&lt;/p&gt;

&lt;p&gt;Other pruning techniques can also be applied programmatically. Removing punctuation, numbers, HTML tags, URLs and email addresses, or non-alphabetical characters are all valid pruning techniques that can be straightforward to apply. Here is a function that does just that:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove URLs
&lt;/span&gt;    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http\S+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Remove email addresses
&lt;/span&gt;    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\S+@\S+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Remove everything that's not a letter (a-z, A-Z)
&lt;/span&gt;    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[^a-zA-Z\s]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Remove whitespace, tabs, and new lines
&lt;/span&gt;    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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;text&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What measure of length reduction might we be able to get from this additional processing? Applying these techniques to the remaining characters of Tech Leader Docs results in just 75,217 characters; an overall reduction of about 28% from the original text.&lt;/p&gt;

&lt;p&gt;More opinionated pruning, such as removing short words or specific words or phrases, can be tailored to a specific use case. These don’t lend themselves well to general functions, however.&lt;/p&gt;

&lt;p&gt;Now that you have some text processing techniques in your toolkit, let’s look at how a reduction in characters translates to fewer tokens used when it comes to ChatGPT. To understand this, we’ll examine Byte-Pair Encoding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Byte-Pair Encoding (BPE)
&lt;/h2&gt;

&lt;p&gt;Byte-Pair Encoding (BPE) is a subword tokenization method. It was originally introduced for data compression but has since been adapted for tokenization in NLP tasks. It allows representing common words as tokens and splits more rare words into subword units. This enables a balance between character-level and word-level tokenization.&lt;/p&gt;

&lt;p&gt;Let’s make that more concrete. Imagine you have a big box of LEGO bricks, and each brick represents a single letter or character. You’re tasked with building words using these LEGO bricks. At first, you might start by connecting individual bricks to form words. But over time, you notice that certain combinations of bricks (or characters) keep appearing together frequently, like “th” in “the” or “ing” in “running.”&lt;/p&gt;

&lt;p&gt;BPE is like a smart LEGO-building buddy who suggests, “Hey, since ’th’ and ‘ing’ keep appearing together a lot, why don’t we glue them together and treat them as a single piece?” This way, the next time you want to build a word with “the” or “running,” you can use these glued-together pieces, making the process faster and more efficient.&lt;/p&gt;

&lt;p&gt;Colloquially, the BPE algorithm looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with single characters.&lt;/li&gt;
&lt;li&gt;Observe which pairs of characters frequently appear together.&lt;/li&gt;
&lt;li&gt;Merge those frequent pairs together to treat them as one unit.&lt;/li&gt;
&lt;li&gt;Repeat this process until you have a mix of single characters and frequently occurring character combinations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;BPE is a particularly powerful tokenization method, especially when dealing with diverse and extensive vocabularies. Here’s why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handling rare words: Traditional tokenization methods might stumble upon rare or out-of-vocabulary words. BPE, with its ability to break words down into frequent subword units, can represent these words without needing to have seen them before.&lt;/li&gt;
&lt;li&gt;Efficiency: By representing frequent word parts as single tokens, BPE can compress text more effectively. This is especially useful for models like ChatGPT, where token limits apply.&lt;/li&gt;
&lt;li&gt;Adaptability: BPE is language-agnostic. It doesn’t rely on predefined dictionaries or vocabularies. Instead, it learns from the data, making it adaptable to various languages and contexts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In essence, BPE strikes a balance, offering the granularity of character-level tokenization and the context-awareness of word-level tokenization. This hybrid approach ensures that NLP models like ChatGPT can understand a wide range of texts while maintaining computational efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending lots of text to ChatGPT
&lt;/h2&gt;

&lt;p&gt;At time of writing, a message to ChatGPT via its web interface has a maximum token length of 4,096 tokens. If we assume the prior mentioned percent reduction as an average, this means you could reduce text of up to 5,712 tokens down to the appropriate size with just text preprocessing.&lt;/p&gt;

&lt;p&gt;What about when this isn’t enough? Beyond text preprocessing, larger input can be sent in chunks using the OpenAI API. In my next post, I’ll show you how to build a Python module that does exactly that.&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>openai</category>
      <category>development</category>
      <category>nlp</category>
    </item>
    <item>
      <title>Git branching for small teams</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Mon, 23 May 2022 12:12:48 +0000</pubDate>
      <link>https://dev.to/victoria/git-branching-for-small-teams-2n64</link>
      <guid>https://dev.to/victoria/git-branching-for-small-teams-2n64</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvictoria.dev%2Fblog%2Fgit-branching-for-small-teams%2Fcover_hu2c7b131ca42731bc004a5709524962fe_15416_640x0_resize_box_3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvictoria.dev%2Fblog%2Fgit-branching-for-small-teams%2Fcover_hu2c7b131ca42731bc004a5709524962fe_15416_640x0_resize_box_3.png" width="800" height="400"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Here’s a practice I use personally and encourage within my open source projects and any small teams I run for work. I’ve seen major elements of it presented under a few different names: &lt;a href="https://trunkbaseddevelopment.com/short-lived-feature-branches/" rel="noopener noreferrer"&gt;Short-Lived Feature Branch&lt;/a&gt; flow, &lt;a href="https://docs.github.com/en/get-started/quickstart/github-flow" rel="noopener noreferrer"&gt;GitHub flow&lt;/a&gt; (not to be confused with GitFlow), and &lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow" rel="noopener noreferrer"&gt;Feature Branch Workflow&lt;/a&gt; are some. Having implemented features I like from all of these with different teams over the years, I’ll describe the resulting process that I’ve found works best for small teams of about 5-12 people.&lt;/p&gt;

&lt;h2&gt;
  
  
  A protected main branch
&lt;/h2&gt;

&lt;p&gt;To support continuous delivery, no human should have direct push permissions on your &lt;code&gt;master&lt;/code&gt; branch. If you develop on GitHub, the latest tag of this branch gets deployed when you &lt;a href="https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release" rel="noopener noreferrer"&gt;create a release&lt;/a&gt; – which is hopefully very often, and very automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  One issue, one branch, one PR
&lt;/h2&gt;

&lt;p&gt;You’re already doing a great job of tracking future features and current bugs as issues (right?). To take a quick aside, an issue is a well-defined piece of work that can be merged to the main branch and deployed without breaking anything. It could be a new piece of functionality, a button component update, or a bug fix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvictoria.dev%2Fblog%2Fgit-branching-for-small-teams%2Fcover.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvictoria.dev%2Fblog%2Fgit-branching-for-small-teams%2Fcover.png" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Author's illustration of issue branches and releases from master.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A short-lived branch-per-issue helps ensure that its resulting pull request doesn’t get too large, making it unwieldy and hard to review carefully. The definition of “short” varies depending on the team or project’s development velocity: for a small team producing a commercial app (like a startup), the time from issue branch creation to PR probably won’t exceed a week. For open source projects like the &lt;a href="https://github.com/OWASP/wstg" rel="noopener noreferrer"&gt;OWASP WSTG&lt;/a&gt; that depends on volunteers working around busy schedules, branches may live for a few weeks to a few months, depending on the contributor. Generally, strive to iterate in as little time as possible.&lt;/p&gt;

&lt;p&gt;Here’s what this looks like practically. For an issue named &lt;strong&gt;(#28) Add user settings page&lt;/strong&gt;, check out a new branch from &lt;code&gt;master&lt;/code&gt;:&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;# Get all the latest work locally&lt;/span&gt;
git checkout master
git pull
&lt;span class="c"&gt;# Start your new branch from master&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; 28/add-settings-page

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Work on the issue, and periodically merge &lt;code&gt;master&lt;/code&gt; to fix and avoid other conflicts:&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;# Commit to your issue branch&lt;/span&gt;
git commit ...
&lt;span class="c"&gt;# Get the latest work on master&lt;/span&gt;
git checkout master
git pull
&lt;span class="c"&gt;# Return to your issue branch and merge in master&lt;/span&gt;
git checkout 28/add-settings-page
git merge master

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may prefer to use rebasing instead of merging in &lt;code&gt;master&lt;/code&gt;. This happens to be my personal preference as well, however, I’ve found that people generally seem to have a harder time wrapping their heads around how rebasing works than they do with merging. Interactive rebasing can easily introduce confusing errors, and rewriting history can be confusing to begin with. Since I’m all about reducing cognitive load in developers’ processes in general, I recommend using a merge strategy.&lt;/p&gt;

&lt;p&gt;When the issue work is ready to PR, open the request against &lt;code&gt;master&lt;/code&gt;. Automated tests run. Teammates review the work (using &lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request#adding-line-comments-to-a-pull-request" rel="noopener noreferrer"&gt;inline comments and suggestions&lt;/a&gt; if you’re on GitHub). Depending on the project, you may deploy a preview version as well.&lt;/p&gt;

&lt;p&gt;Once everything checks out, the PR is merged, the issue is closed, and the branch is deleted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it clean
&lt;/h2&gt;

&lt;p&gt;Some common pitfalls I’ve seen that can undermine this flow are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Creating feature branches off of other feature/issue branches.&lt;/strong&gt; This is a result of poor organization and prioritization. To avoid confusing conflicts and dependencies, always branch off the most up-to-date &lt;code&gt;master&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Letting the issue branch live &lt;em&gt;just a little longer&lt;/em&gt;.&lt;/strong&gt; This results in scope creep and huge, confusing PRs that take a lot of time and mental effort to review. Keep branches tightly scoped to the one issue they’re meant to close.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not deleting merged branches.&lt;/strong&gt; There’s no reason to leave them about – all the work is in &lt;code&gt;master&lt;/code&gt;. Not removing branches that are stale or have already been merged can cause confusion and make it more difficult than necessary to differentiate new ones.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If this sounds like a process you’d use, or if you have anything to add, let me know in the comments! &lt;/p&gt;

</description>
      <category>productivity</category>
      <category>git</category>
      <category>startup</category>
    </item>
    <item>
      <title>My paper to-do strategy</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Mon, 25 Oct 2021 12:17:32 +0000</pubDate>
      <link>https://dev.to/victoria/my-paper-to-do-strategy-fmk</link>
      <guid>https://dev.to/victoria/my-paper-to-do-strategy-fmk</guid>
      <description>&lt;p&gt;Coding up a to-do app may be the Hello, World of every framework, but when it comes to actually tracking tasks effectively (knock ‘em out not stack ‘em up) there’s no app that keeps things front of mind better than an open notebook on your desk.&lt;/p&gt;

&lt;p&gt;Here’s my stupid-simple strategy for tracking and checking off my to-do list.&lt;/p&gt;

&lt;h2&gt;
  
  
  One page at a time
&lt;/h2&gt;

&lt;p&gt;Plenty of methodologies recommend using sections or different pages of your book for monthly, weekly, and daily views; others advocate for creating sections for each category, such as “Home Tasks” and “Work Tasks” and other such time-wasters. All of this is unnecessary.&lt;/p&gt;

&lt;p&gt;A to-do list works because it’s in your face and hard to miss. When you write things down on different pages, they become easy to miss. Don’t do that.&lt;/p&gt;

&lt;p&gt;Use one page at a time. Write down one task under another. Don’t sort them, prioritize them (yet), or categorize anything. Just write them down on the current page, where you’re guaranteed to look when you lay eyes on your notebook next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intuitive notation
&lt;/h2&gt;

&lt;p&gt;I use my notebook for two things: short notes (just a bit of information – nothing to do) and tasks (something to do). This translates to a notation system of three possible states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s a note, indicated with a bullet point&lt;/li&gt;
&lt;li&gt;It’s a new task, indicated with a checkbox&lt;/li&gt;
&lt;li&gt;It’s a completed task, with the checkbox checked and the line struck out (because strike-throughs are &lt;em&gt;satisfying&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvictoria.dev%2Fblog%2Fmy-paper-to-do-strategy%2Fa9ccelphZv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvictoria.dev%2Fblog%2Fmy-paper-to-do-strategy%2Fa9ccelphZv.jpeg" alt="A picture of my task list" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use a checkbox to distinguish tasks from notes because I’m an old-school HTML fan, but you do you.&lt;/p&gt;

&lt;p&gt;You may like to add your own embellishments to this: I sometimes denote an urgent item with an asterisk. You might like to use a color pen or highlighter (avoid the bullet journal rabbit hole – another time-waster). Just keep it simple, repeatable, and intuitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it’s time to turn the page
&lt;/h2&gt;

&lt;p&gt;When life gets busy, you might fill up a page pretty quickly. If one or two tasks haven’t yet been crossed off, they’re liable to be forgotten. You can avoid this by carrying tasks over to the next page.&lt;/p&gt;

&lt;p&gt;It’s straightforward: cross out the task on the page that’s filled up. Turn the page and write it down there again.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;That’s silly,&lt;/em&gt; you might say, &lt;em&gt;that’s a waste of energy! By the time I write it down all over again, I could’ve done half of it already.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;I’ll wait.&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;The clever bit about carrying a task over is taking the opportunity to evaluate it. If the task is really a five-minute thing, more often than not, I go ahead and take care of it right there and then. If it’s a longer endeavor, the friction of writing it down again gives me the chance to answer the question of whether it’s something I feel strongly about doing (and hence whether it’s really important that I do it at all). It might not be, and that’s fine. I cross it out and don’t do it. If it is an important task, carrying it over means it remains front of mind until I can make the time to get it done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time well spent doing
&lt;/h2&gt;

&lt;p&gt;I’ve explored a myriad of task list apps, pre-printed to-do lists and journals, and all kinds of digital notes for tracking work. I consistently keep returning to the feel of pen on paper and an open notebook on my desk. Why? Minimal cognitive load.&lt;/p&gt;

&lt;p&gt;No time spent categorizing and labeling tasks in a complicated system. No time spent remembering how to open that app, where you stored that &lt;code&gt;todo.txt&lt;/code&gt; file, or deciding whether to write something down under your weekly or daily plan. No tasks lost in an invisible backlog that grows over the years, becoming more and more infeasible.&lt;/p&gt;

&lt;p&gt;Just pen and paper, one page at a time, and the satisfaction of getting things done.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>devtips</category>
    </item>
    <item>
      <title>Measuring productivity with GitHub issues</title>
      <dc:creator>Victoria Drake</dc:creator>
      <pubDate>Mon, 30 Aug 2021 05:35:02 +0000</pubDate>
      <link>https://dev.to/victoria/measuring-productivity-with-github-issues-4lof</link>
      <guid>https://dev.to/victoria/measuring-productivity-with-github-issues-4lof</guid>
      <description>&lt;p&gt;How long does it take for a bug to get squashed, or for a pull request to be merged? What kind of issues take the longest to close?&lt;/p&gt;

&lt;p&gt;Most organizations want to improve productivity and output, but few technical teams seem to take a data-driven approach to discovering productivity bottlenecks. If you’re looking to improve development velocity, a couple key metrics could help your team get unblocked. Here’s how you can apply a smidge of data science to visualize how your repository is doing, and where improvements can be made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting quality data
&lt;/h2&gt;

&lt;p&gt;The first and most difficult part, as any data scientist would likely tell you, is ensuring the quality of your data. It’s especially important to consider consistency: are dates throughout the dataset presented in a consistent format? Have tags or labels been applied under consistent rules? Does the dataset contain repeated values, empty values, or unmatched types?&lt;/p&gt;

&lt;p&gt;If your repository has previously changed up processes or standards, consider the timeframe of the data you collect. If labeling issues is done arbitrarily, those may not be a useful feature. While cleaning data is outside the scope of this article, I can, at least, help you painlessly collect it.&lt;/p&gt;

&lt;p&gt;I wrote a straightforward &lt;a href="https://github.com/victoriadrake/got-issues/" rel="noopener noreferrer"&gt;Python utility&lt;/a&gt; that uses the GitHub API to pull data for any repository. You can use this on the command line and output the data to a file. It uses the &lt;a href="https://docs.github.com/en/rest/reference/issues#list-repository-issues" rel="noopener noreferrer"&gt;list repository issues endpoint (docs)&lt;/a&gt;, which, perhaps confusingly, includes both issues and pull requests (PRs) for the repository. I get my data like this:&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="nv"&gt;$ &lt;/span&gt;python fetch.py &lt;span class="nt"&gt;-h&lt;/span&gt;
usage: fetch.py &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-h&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--token&lt;/span&gt; TOKEN] repository months
&lt;span class="nv"&gt;$ &lt;/span&gt;python fetch.py OWASP/wstg 24 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; data.json

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the GitHub API means less worry about standardization, for example, all the dates are expressed as ISO 8601. Now that you have some data to process, it’s time to play with Pandas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plotting with Pandas
&lt;/h2&gt;

&lt;p&gt;You can use a &lt;a href="https://jupyter.org/" rel="noopener noreferrer"&gt;Jupyter Notebook&lt;/a&gt; to do some simple calculations and data visualization.&lt;/p&gt;

&lt;p&gt;First, create the Notebook file:&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;touch &lt;/span&gt;stats.ipynb

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the file in your favorite IDE, or in your browser by running &lt;code&gt;jupyter notebook&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the first code cell, import Pandas and load your data:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then run that cell to see a preview of the data you collected.&lt;/p&gt;

&lt;p&gt;Pandas is a &lt;a href="https://pandas.pydata.org/pandas-docs/stable/index.html" rel="noopener noreferrer"&gt;well-documented&lt;/a&gt; data analysis library. With a little imagination and a few keyword searches, you can begin to measure all kinds of repository metrics. For this walk-through, here’s how you can calculate and create a graph that shows the number of days an issue or PR remains open in your repository.&lt;/p&gt;

&lt;p&gt;Create a new code cell and, for each item in your &lt;a href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.html" rel="noopener noreferrer"&gt;Series&lt;/a&gt;, subtract the date it was closed from the date it was created:&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="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;closed_at&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.describe.html" rel="noopener noreferrer"&gt;&lt;code&gt;Series.describe()&lt;/code&gt;&lt;/a&gt; will give you some summary statistics that look something like these (from &lt;a href="https://github.com/python/mypy" rel="noopener noreferrer"&gt;mypy on GitHub&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;count 514
mean 5 days 08:04:17.239299610
std 14 days 12:04:22.979308668
min 0 days 00:00:09
25% 0 days 00:47:46.250000
50% 0 days 06:18:47
75% 2 days 20:22:49.250000
max 102 days 20:56:30

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.plot.html?" rel="noopener noreferrer"&gt;&lt;code&gt;Series.plot()&lt;/code&gt;&lt;/a&gt; uses a specified plotting backend (&lt;code&gt;matplotlib&lt;/code&gt; by default) to visualize your data. A histogram can be a helpful way to examine issue duration:&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="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will plot a histogram that represents the frequency distribution of issues over days, which is one way you can tell how long most issues take to close. For example, mypy seems to handle the majority of issues and PRs within 10 days, with some outliers taking more than three months:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvictoria.dev%2Fblog%2Fmeasuring-productivity-with-github-issues%2Fplot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvictoria.dev%2Fblog%2Fmeasuring-productivity-with-github-issues%2Fplot.png" alt="Histogram for mypy issues over the last six months" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It would be interesting to visualize other repository data, such as its most frequent contributors, or most often used labels. Does a relationship exist between the author or reviewers of an issue and how quickly it is resolved? Does the presence of particular labels predict anything about the duration of the issue?&lt;/p&gt;

&lt;h2&gt;
  
  
  You aim for what you measure
&lt;/h2&gt;

&lt;p&gt;Now that you have some data-driven superpowers, remember that it comes with great responsibility. Deciding what to measure is just as, if not more, important than measuring it.&lt;/p&gt;

&lt;p&gt;Consider how to translate the numbers you gather into productivity improvements. For example, if your metric is closing issues and PRs faster, what actions can you take to encourage the right behavior in your teams? I’d suggest encouraging issues to be clearly defined, and pull requests to be small and have a well-contained scope, making them easier to understand and review.&lt;/p&gt;

&lt;p&gt;To prepare to accurately take measurements for your repository, establish consistent standards for labels, tags, milestones, and other features you might want to examine. Remember that meaningful results are more easily gleaned from higher quality data.&lt;/p&gt;

&lt;p&gt;Finally, have fun exercising your data science skills. Who knows what you can discover and improve upon next!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>devtips</category>
    </item>
  </channel>
</rss>
