How I work: Pocket

02 Sep 2022 in Setup

There are plenty of tools (like Pocket) that sell us on the dream of saving things for later if we don't have chance to read or watch it there and then. Unfortunately, these tools usually end up being a graveyard of forgotten links that we never look at again and we'd have been better off just closing the tab.

I saved my first link to Pocket in October 2012 (I was an Instapaper user before then), and have read 15,433 items (with 124 unread) since then.

How do I do it? Let me introduce you to pocket-tagger-cli

Categorizing content

Pocket Tagger is a CLI tool that I built back in 2018 to help automatically categorise saved items in Pocket using tags based on predefined rules. It allows you to define regular expressions and define tags based on those expressions. You can express things like:

  • Tag it with length-medium if it's > 1500 and < 5000 characters
  • Tag t with has-slides if the URL contains slideshare.net or speakerdeck.com
  • Tag it with github if the page mentions github but not github-actions (this is a separate tag)

The rules can be executed against the item URL, text content or raw HTML (I usually use URL and text content).

Here's a sample configuration that will execure the rules above. If multiple entries are provided (e.g. has-slides) then they are evaluated as an OR. If you nest them in another array (e.g. github), then they're evaluated as an AND.

json
{
"regexes": {
"length-medium": "^[\\s\\S]{1501,5000}$",
"slideshare-url": "^https://www.slideshare.net",
"speakerdeck-url": "^https://www.speakerdeck.com",
"github": "github.com",
"github-actions": "github actions"
},
"tags": {
"url": {
"has-slides": ["slideshare-url", "speakerdeck-url"],
"github": [["github", "!github-actions"]]
},
"content": {
"length-medium": ["length-medium"]
},
"html": {}
}
}

This is a very small configuration that should get you started. If you want to see my full configuration (which is still evolving) scroll to the end of this post.

Running Pocket Tagger

Pocket tagger isn't a particularly user-friendly tool. It involves registering for your own Pocket developer app and obtaining your own authentication credentials. I've built some tooling to help, but it still involves having Node.js installed and running some CLI commands.

If you want to run Pocket tagger yourself, here are the instructions:

  1. Create an application in the Pocket Apps section
  2. Run npx pocket-auth-cli <consumer_key>to obtain an access_token
  3. Install pocket-tagger-cli
  4. Generate a ruleset with pocket-tagger --generate ~/.pocket/tagger.json
  5. Run DEBUG="pocket-tagger*" pocket-tagger and watch as it fetches all your Pocket entries and categorises them (this may take a little while)

Michael's configuration

I mentioned above that my configuration has been developed over time and is considerably larger than the sample set of rules provided. I try to segment things based on context and batch process things when reading. My rough split of content is:

  • Sites such as Goodreads (probably a book to buy), Hacker News (yes, I read the comments), reddit (easy-reading) or BoardGameGeek (games to buy)
  • Leadership related reading (New York Times, Harvard Business Review, Fast Company, Inc, Forbes etc)
  • Cloud related reading (AWS, GCP, Azure)
  • Tooling related content (Ansible, Chef, Prometheus, Jenkins, VSCode)
  • GitHub related content (I'm a member of the Stars programme)
  • Work related content (Kong, DevRel, DevX, Documentation, API Gateway, OpenAPI)
  • Media to check out (YouTube, Spotify)
  • Fitness related things (Fitness, Gym)

I then choose which tags to read based on what I'm doing. I'll check my media tag during my lunch break, or read work related content between calls. Leadership related content is my go-to when I'm in the queue at the supermarket, and GitHub related content gets read on a Monday morning ahead of a weekly Stars call. Finally, I tend to save the length-essay items for when I'm travelling by train or by plane. Having a long piece of content to read is a great way to get absorbed and time passes very quickly.

Here's my current configuration, in case you want to take any inspiration from it:

json
{
"regexes": {
"length-too-short": "^[\\s\\S]{0,5}$",
"length-short": "^[\\s\\S]{6,1500}$",
"length-medium": "^[\\s\\S]{1501,5000}$",
"length-long": "^[\\s\\S]{5001,15000}$",
"length-essay": "^[\\s\\S]{15001,100000}$",
"length-too-long": "^[\\s\\S]{100001,}$",
"code-php": "(\\sphp\\s|<\\?php|composer)",
"code-javascript": "\\sjavascript\\s",
"code-node": "(node\\.js|nodejs)",
"code-golang": "golang",
"tool-composer": "composer",
"tool-ansible": "ansible",
"tool-chef": "\\schef\\s",
"tool-prometheus": "prometheus",
"tool-jenkins": "jenkins",
"tool-docker": "docker",
"tool-kubernetes": "kubernetes",
"call-for-proposals-url": "(https://cfp|http://cfp)",
"call-for-proposals": "(call for proposals|call for papers)",
"openapi": "openapi",
"documentation": "(documentation|docs|portal)",
"management": "(manager|1:1)",
"product-management": "product manage",
"api-management": "api manage",
"api": "api",
"devrel": "(devrel|dev rel|developer relations)",
"devx": "(devx|dx|developer experience|dev experience)",
"kong": "\\s(kong|kuma|mesh|gateway)\\s",
"fitness": "(fitness|workout)",
"gym": "\\s(gym|bench|weights)\\s",
"twitch": "twitch",
"slideshare-url": "slideshare.net",
"speakerdeck-url": "speakerdeck.com",
"slideshare-embed": "https://www.slideshare.net/slideshow/embed_code",
"speakerdeck-embed": "speakerdeck-embed",
"is-pdf": ".pdf$",
"hacker-news": "ycombinator",
"hukd": "hotukdeals.com",
"github": "github.com",
"github-actions": "github actions",
"npm": "npmjs.com",
"twitter": "twitter.com",
"reddit": "reddit.com",
"medium": "medium.com",
"devto": "dev.to",
"hackernoon": "hackernoon.com",
"lifehacker": "lifehacker.com",
"theverge": "theverge.com",
"youtube": "(youtube.com|youtu.be)",
"nytimes": "nytimes.com",
"hbr": "hbr.org",
"fastcompany": "fastcompany.com",
"inc": "inc.com",
"qz": "qz.com",
"entrepreneur": "entrepreneur.com",
"forbes": "forbes.com",
"bloomberg": "bloomberg.com",
"wsj": "wsj.com",
"nordicapis": "nordicapis.com",
"spotify": "spotify.com",
"amazon": "amazon.com",
"papercall": "papercall.io",
"goodreads": "goodreads.com",
"boardgamegeek": "(boardgamegeek.com|bgg)",
"api-gateway": "api gateway",
"aws": "(amazon web service|aws)",
"google-cloud": "(google cloud platform|gcp)",
"azure": "azure",
"work-api": "(rest|api)",
"vscode-marketplace": "marketplace.visualstudio.com",
"vscode": "vscode"
},
"tags": {
"url": {
"openapi": ["openapi"],
"cloud-aws": ["aws"],
"cloud-gcp": ["google-cloud"],
"cloud-azure": ["azure"],
"tool-vscode": ["vscode-marketplace"],
"is-cfp": ["papercall", "call-for-proposals-url"],
"is-pdf": ["is-pdf"],
"has-slides": ["slideshare-url", "speakerdeck-url"],
"site-goodreads": ["goodreads"],
"site-hn": ["hacker-news"],
"site-hukd": ["hukd"],
"site-mediumesque": ["medium", "devto", "hackernoon"],
"site-lifehacker": ["lifehacker"],
"site-twitter": ["twitter"],
"site-reddit": ["reddit"],
"site-bgg": ["boardgamegeek"],
"site-nordicapis": ["nordicapis"],
"motivational": [
"nytimes",
"hbr",
"fastcompany",
"qz",
"inc",
"entrepreneur",
"forbes",
"bloomberg",
"wsj"
],
"fun-read": ["theverge"],
"github": [["github", "!github-actions", "!openapi"]],
"github-actions": ["github-actions"],
"npm": ["npm"],
"amazon": ["amazon"],
"management": ["management"],
"media-youtube": ["youtube"],
"media-spotify": ["spotify"]
},
"content": {
"boardgames": ["boardgamegeek"],
"ops-kubernetes": ["tool-kubernetes"],
"ops-docker": ["tool-docker"],
"cloud-aws": ["aws"],
"cloud-gcp": ["google-cloud"],
"cloud-azure": ["azure"],
"management": [["management", "!product-management", "!api-management"]],
"product-management": ["product-management"],
"api-management": ["api-management"],
"github-actions": ["github-actions"],
"is-cfp": ["papercall", "call-for-proposals"],
"length-error": ["length-too-short", "length-too-long"],
"length-short": ["length-short"],
"length-medium": ["length-medium"],
"length-long": ["length-long"],
"length-essay": ["length-essay"],
"tool-composer": ["tool-composer"],
"tool-ansible": ["tool-ansible"],
"tool-chef": ["tool-chef"],
"tool-prometheus": ["tool-prometheus"],
"tool-jenkins": ["tool-jenkins"],
"tool-vscode": ["vscode-marketplace", "vscode"],
"work-kong": ["kong"],
"work-devx": ["devx"],
"work-devrel": ["devrel"],
"work-documentation": ["documentation"],
"work-api-gateway": ["api-gateway"],
"work-api": [["api", "!openapi"]],
"work-openapi": ["openapi"],
"personal-fitness": ["fitness"],
"personal-gym": ["gym"],
"streaming": ["twitch"]
},
"html": {
"code-php": ["code-php"],
"code-javascript": ["code-javascript"],
"code-node": ["code-node"],
"code-golang": ["code-golang"],
"has-slides": ["slideshare-embed", "speakerdeck-embed"]
}
},
"cache": {
"engine": "file",
"tmpDir": "/tmp/url-tagger",
"ttl": 86400
}
}

Conclusion

Pocket Tagger can't magically create more time for you to read the things you save to Pocket, but it can help you find the right things to read based on your current context. I never want to watch YouTube videos on a train, and I don't want to start a long article if I only have 10 minutes before my next call.

Categorising content allows you to break your backlog down into smaller, accessible chunks of content that you can start to read one by one. Focus on clearing a specific tag, and when that tag is empty you'll get the dopamine kick from finishing something, even if you still have 500 more articles to read later.

PS: I use Reeder on MacOS and iOS as my Pocket client, if you're looking for a client too