diff --git a/404.html b/404.html index e485c35f..5dd4e805 100644 --- a/404.html +++ b/404.html @@ -1,5 +1,14 @@ Whoops! | aviskase -

Whoops!

\ No newline at end of file diff --git a/articles/2015/12/23/how-to-watch-swf-in-linux/index.html b/articles/2015/12/23/how-to-watch-swf-in-linux/index.html index ee063929..14811ede 100644 --- a/articles/2015/12/23/how-to-watch-swf-in-linux/index.html +++ b/articles/2015/12/23/how-to-watch-swf-in-linux/index.html @@ -2,14 +2,14 @@

How to watch SWF in Linux

+23 Dec 2015
1 min

Sometimes people use Jing to record videos for bug reports. This pest is saving them as SWF file. So, here is a simple note on how to open these videos in Linux.

It’s really easy. Firefox can open them (of course, if Shockwave plugin is present). Just download that nasty video and open it in FF, all’s done.

But there is a catch. I don’t know how it’s on Windows, but for Linux you should edit mime types. In order to do that you should create a file ~/.mime.types with this content:

application/x-shockwave-flash  swf swfl
 

That’s all! This way is the easiest, because it works only for the owner of the home directory, where the file was created.

But if you want, you can make this setting global. But be careful, because everything will be reset after an upgrade. You should open the file:

$ sudo nano /usr/share/mime/packages/freedesktop.org.xml
 

and replace this string:

<mime-type type="application/vnd.adobe.flash.movie">
 

with this one:

<mime-type type="application/x-shockwave-flash">
 

Then execute:

$ sudo update-mime-database /usr/share/mime
-
\ No newline at end of file +
newer
\ No newline at end of file diff --git a/articles/2016/01/09/list-of-articles-and-videos-on-api-and-web-services-testing/index.html b/articles/2016/01/09/list-of-articles-and-videos-on-api-and-web-services-testing/index.html index 578f5f44..ad187c4b 100644 --- a/articles/2016/01/09/list-of-articles-and-videos-on-api-and-web-services-testing/index.html +++ b/articles/2016/01/09/list-of-articles-and-videos-on-api-and-web-services-testing/index.html @@ -1,12 +1,297 @@ List of articles and videos on API and web services testing | aviskase -

List of articles and videos on API and web services testing

+9 Jan 2016
+3 mins

There was a great list on now discontinued site qahelp.net. I managed to save it through yandex cache (even google cache and web archive couldn’t help).

Difference between API and web services

SOAP and REST

API and web services testing

Deep dive into REST API

API security testing

Service virtualization

Introduction to microservices

Microservices testing

More on API

older   ·   ·   ·   -newer
\ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2016/04/24/testing-knowledge-transfer-outline/index.html b/articles/2016/04/24/testing-knowledge-transfer-outline/index.html index 63e53c1a..087d5723 100644 --- a/articles/2016/04/24/testing-knowledge-transfer-outline/index.html +++ b/articles/2016/04/24/testing-knowledge-transfer-outline/index.html @@ -1,9 +1,9 @@ Testing knowledge transfer (outline) | aviskase -

Testing knowledge transfer (outline)

+24 Apr 2016
+1 min

There is an excellent blog post (ru) by Elena Poplouhina with the list of what not to forget to tell about testing to project newcomers. So I’ve made an English translation with some correction. Here it is as an outline.

older   ·   ·   ·   -newer
\ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2016/05/02/coverage-metrics/index.html b/articles/2016/05/02/coverage-metrics/index.html index d40caa67..f62336f0 100644 --- a/articles/2016/05/02/coverage-metrics/index.html +++ b/articles/2016/05/02/coverage-metrics/index.html @@ -1,12 +1,24 @@ Coverage metrics | aviskase

Coverage metrics

+2 May 2016
+4 mins

There are two strange metrics on the project where I work: how much is tested and how much works. Every time I should update these two numbers my head explodes. I don’t like them at all.

What means “how much is tested”?

Traditionally we are writing a percentage of completed checks out of all checks in the checklist. I don’t like this tradition, because checks are different. Here you should just assert error’s text message, and there you should produce this error under some conditions.

I see this solution. Every check should have corresponding number like a story point. Let’s call it a test point. To check text there is 1 tp, and to reproduce an error — 3 tp. That way the whole checklist costs 4 tp. When we are going through the checklist, a tester should write how much is tested for every check. For example, text is asserted — 1 pt. But reproducing is done only for several scenarios, so only 2 tp. In total, we completed 3 out of 4 test points.

What means “how much works”?

Based on what are other testers are doing, it is a number of “green” (or passed) checks out of all checks.

Consider this situation: test some kind of import. Test session is started, and voila, there no “Import” button. That’s a blocker and no more checks could be done. What we are writing for “how much works”? Zero. A developer fixes the button. Checks are flying, everything is perfect, we are writing 100% (a miracle).

Here is a question: is it correct that we put zero after the first session? After all, it was only a button, everything else was working already. I think it’s correct. But we should rename this metric, because it shows not how much works, but how much is available to a user and to what extent.

And what is in result?

Checklists are measured in test points, availability metric is renamed. Now watch carefully. We are throwing away percentages. After all, all checklists are different. Why should we use percentages, when they can’t show reality. 20% of a small task with 10 tp — not bad, 80% of a task with 1000 tp — can ruin a release. And strictly speaking this method is using not ratio, but interval scale. You can’t multiply and divide in this scale, therefore, can’t calculate percentage.

Guru talks

Michael Bolton writes a lot about choosing right scales and using them properly. Recently there was yet another article. His positions is that in testing even interval scale is too much, nominal and ordinal are more correct. There is an excellent example in that article, so I just leave it here:

  • ⚪ Level 0: we know nothing at all about this area of the product.
  • 😶 Level 1: we have done a very cursory evaluation of this area. Smoke- or sanity-level; we’ve visited this feature and had a brief look at it, but we don’t really know very much about it; we haven’t probed it in any real depth.
  • 😐 Level 2: we’ve had a reasonable look at this area, although we haven’t gone all the way deep. We’ve examined the common, the core, the critical, the happy paths, the handling of everyday errors or exceptions. We’ve pretty familiar with this area. We’ve done the kind of testing that would expose some significant bugs, if they were there.
  • 😎 Level 3: we’ve really kicked this area harshly and hard. We’ve looked at unusual and complex conditions or states. We’ve probed deeply for subtle or hidden bugs. We’ve exposed the product to the extreme, the exceptional, the rare, the improbable. We’ve looked for bugs that are deep in the corners or hidden in the dark. If there were a serious bug, we’re pretty sure we would have found it by now.
older   ·   ·   ·   -newer
\ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2016/05/09/thoughts-on-dear-evil-tester/index.html b/articles/2016/05/09/thoughts-on-dear-evil-tester/index.html index ad9857e7..870bd93a 100644 --- a/articles/2016/05/09/thoughts-on-dear-evil-tester/index.html +++ b/articles/2016/05/09/thoughts-on-dear-evil-tester/index.html @@ -5,11 +5,11 @@ The first part is kick-ass. You can just quote a random sentence from it and it will be great. For instance, I really want to use this one as a personal motto:">

Thoughts on “Dear Evil Tester”

+9 May 2016
+2 mins

Recently I’ve read a book “Dear Evil Tester” by Alan Richardson. The book has three parts: published letters, unpublished, essays.

The first part is kick-ass. You can just quote a random sentence from it and it will be great.

For instance, I really want to use this one as a personal motto:

The only principle I’m prepared to absolutely commit to, with absolute certainty, is that I can change my mind.

And this one clearly shows the moment when everything starts flying to hell:

If a person has the power to cause the project to fail, then they can say ‘testing is not required’, at the point they make the decision to doom the project.

Oh, how often it is when a check is “done”, then no, not a single step back, there will be no time for restesting and rechecking:

And as we all know, once a ‘Test Case’ is done, it can never be undone.

Guru by themselves, so classic:

Enumerate everything that you do, and only you do, and then define ‘true’ Exploratory Testing as the specific combination of items that you enumerated

The second part wasn’t so interesting. Yep, there are more practical and philosophical ideas, but they weren’t evil enough. It also applies to the third part.

Nevertheless, it’s the book you’ll want to reread — cute devil’s doodles and provocative style are awesome. And you can buy it on Leanpub.

older   ·   ·   ·   -newer
\ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2016/06/06/test-planning-questions-by-google-outline/index.html b/articles/2016/06/06/test-planning-questions-by-google-outline/index.html index ea1b3045..dfacdb14 100644 --- a/articles/2016/06/06/test-planning-questions-by-google-outline/index.html +++ b/articles/2016/06/06/test-planning-questions-by-google-outline/index.html @@ -1,9 +1,9 @@ Test planning: questions by Google (outline) | aviskase -

Test planning: questions by Google (outline)

+6 Jun 2016
+1 min

Google Testing Blog made all testers happy one more time. The article provides a comprehensive list of questions to be asked before writing a test plan (or a test strategy). So I’ve made an outline to share.

older   ·   ·   ·   -newer
\ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2016/07/17/course-intro-to-devops-by-udacity/index.html b/articles/2016/07/17/course-intro-to-devops-by-udacity/index.html index 2aece5ba..1c56d733 100644 --- a/articles/2016/07/17/course-intro-to-devops-by-udacity/index.html +++ b/articles/2016/07/17/course-intro-to-devops-by-udacity/index.html @@ -1,15 +1,30 @@ Course “Intro to DevOps” by Udacity | aviskase

Course “Intro to DevOps” by Udacity

+17 Jul 2016
+4 mins

I love MOOCs: Coursera, Udacity, Stepic. There are so many courses to watch for entire life. Just now watched a course Intro to DevOps by Udacity.

The course is concise and comprehensive. Here are some notes I’ve made.


DevOps

DevOps is the practice of operations and development engineers participating together in the entire service life-cycle, from design through the development process to production support.

DevOps is also characterized by operations staff making use many of the same techniques as developers for their systems work.

CommitStrip — what DevOps is not

Components that make up DevOps — CAMS:

  • Communication — agile communications, lean, respect
  • Automation — deployment, testing, integration
  • Measurement — monitoring, useful logs, biz metrics, usefulness of tools & processes
  • Sharing — shared view of goals, problems, and benefits

If you can’t measure it, you can’t improve it.

Solving the environment problem

  1. Golden image
    • more work up front — large install image must be regenerated for any change
    • much faster installation/boot
  2. Configuration management
    • lighter build process — integration is done at install/initial boot time
    • slower start up process
  3. Combination of 1 & 2

Monitoring

Monitoring data sources:

  • external probing, test queries
  • application levels stats (queries per second, latency)
  • environment stats (JVM memory profile)
  • host/container stats (load average, disk errors)

Monitoring data products:

  • alerting
  • performance analysis
  • capacity prediction
  • growth measurement
  • debugging metrics

Monitoring systems

Additional resources

Notable books

Notable presentations

  • What DevOps means to me — an explanation of the components that make up CAMS (Culture, Automation, Measurement, Sharing), as well additional thoughts on what DevOps is and is not — by John Willis
  • dev2ops — delivering change in a DevOps and cloud world
  • the agile admin — blog on topics of DevOps, agile operations, cloud computing, infrastructure automation, Web security (especially AppSec), transparency, open source, monitoring, Web performance optimization, and more
  • The DevOps checklist — this checklist is comprised of 48 items you can use to gauge the maturity of your software delivery competency, and form a baseline to measure your future improvements
  • DevOps — A Crash Course by Matt Stratton. A lot of links to good resources on DevOps topics.

Additional resources by Nutanix

  • Nagios and Zabbix — comprehensive solutions for monitoring large infrastructure, but maybe too big and complex for small projects
  • Graphite — open-source database and a graphing solution for storing and displaying monitoring data
  • InfluxDB — an open-source distributed time series database for metrics, events, and analytics
  • StatsD — simple daemon for easy stats aggregation, by Etsy. Read about the philosophy behind it in the article by it’s creators — Measure Anything, Measure Everything
  • Grafana — metrics dashboard and graph editor for Graphite and InfluxDB
  • PagerDuty — incident resolution life-cycle management platform that integrates with over 100 other systems to streamline the process for large organizations
  • Logstash — log storage and search system, works well with — Kibana graphing and visualization software
older   ·   ·   ·   -newer
\ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2016/07/17/course-intro-to-devops-by-udacity/monitoring_systems_hu54ef27e82d2f48967dcaf8478459fe1e_32928_653x283_resize_q75_h2_box_3.webp b/articles/2016/07/17/course-intro-to-devops-by-udacity/monitoring_systems_hu6634930580161807046.webp similarity index 100% rename from articles/2016/07/17/course-intro-to-devops-by-udacity/monitoring_systems_hu54ef27e82d2f48967dcaf8478459fe1e_32928_653x283_resize_q75_h2_box_3.webp rename to articles/2016/07/17/course-intro-to-devops-by-udacity/monitoring_systems_hu6634930580161807046.webp diff --git a/articles/2016/07/27/notes-for-course-intro-to-linux/index.html b/articles/2016/07/27/notes-for-course-intro-to-linux/index.html index cee8522d..6a381ed0 100644 --- a/articles/2016/07/27/notes-for-course-intro-to-linux/index.html +++ b/articles/2016/07/27/notes-for-course-intro-to-linux/index.html @@ -1,9 +1,9 @@ Notes for course “Intro to Linux” | aviskase -

Notes for course “Intro to Linux”

+27 Jul 2016
2 mins

If there is a Linux course on a platform, I’ll always watch it. Those who know me are aware that I am using some kind of Debian-based distributive full time — I’m not a hardcore fan, but I like it here. One might ask: why am I watching all these courses when they are mostly for beginners? Answer is simple: repetition is the mother of learning, plus there are always some tricks that you forget or can become more interesting with time.

So it happened with Intro to Linux(ru) on Russian platform Stepic. First, I’ve got month license for any JetBrains IDE by solving some exercises and that’s cool. Second, cute guys from Bioinformatics Institute made me adore tmux and almost persuaded to look at vim. Almost.

And now as usual, some notes to not to forget. If something looks like a magic: read books or watch some course!


Run program in background:

program &
 

Check if link is available:

wget --spider somelink
 

Download files by links from a text file:

wget -i some-textfile
@@ -11,6 +11,6 @@
 

Create several directories / files at once:

mkdir {dir1,dir2,dir3,dir4}
 touch {fileA,fileB}.txt
 

tmux — terminal multiplexor

  • Go to commands mode: Ctrl+b
  • Create new window: commands mode and c
  • Create vertical split: commands mode and %
  • Create horizontal split: commands mode and "
  • Go to window number 1 … 9: commands mode and 1 -… 9
  • Go to next window: commands mode and n
  • Go to previous window: commands mode and p
  • Close window or split: commands mode and x
  • Detach (temporal exit): commands mode and d
  • Return to tmux: tmux attach / tmux a
  • List all running tmux: tmux list-sessions
older +… 9
  • Go to next window: commands mode and n
  • Go to previous window: commands mode and p
  • Close window or split: commands mode and x
  • Detach (temporal exit): commands mode and d
  • Return to tmux: tmux attach / tmux a
  • List all running tmux: tmux list-sessions
  • older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2016/07/30/thoughts-on-the-a-word/index.html b/articles/2016/07/30/thoughts-on-the-a-word/index.html index 55a1f5b4..fe030cd9 100644 --- a/articles/2016/07/30/thoughts-on-the-a-word/index.html +++ b/articles/2016/07/30/thoughts-on-the-a-word/index.html @@ -1,12 +1,12 @@ Thoughts on “The ‘A’ Word” | aviskase

    Thoughts on “The ‘A’ Word”

    +30 Jul 2016
    +5 mins

    Alan Page is known as one of the authors of “How We Test Software at Microsoft”. But there is another good book and it’s called “The ‘A’ Word”. You can buy it on LeanPub.

    The book is about automation in testing, but not how to do it — it’s about how to think about it. It’s short, just 58 pages, but very dense with ideas and Alan’s opinions.

    As I am not qualified to give an opinion on automation topics (because I don’t have much experience with it), I’ve just gathered some notes for future referencing. Sections are divided by chapters.


    Sometimes, testers use programming skills to help their testing. Sometimes, that code automates some application functionality. That’s it.

    Testing: failing to succeed

    There is a very famous concept called “Orders of Ignorance” introduced by Phillip Glen Armour (more here). Chapter’s idea is that mostly tests are done on 0OI level, but we should never forget about 2OI test. 0OI is a lack of ignorance (I know) and 2OI is a lack of awareness (I don’t know what I don’t know).

    0OI tests are knowledge-proving tests, while 2OI tests are knowledge-acquiring tests.

    The robots are taking over

    Humans fail when they don’t use automation to solve problems impossible or impractical for manual efforts.

    Automation fails when it tries to do or verify something that’s more suited for a human evaluation.

    To automate …?

    Good testers test first — or at the very least they think of tests first.

    Great testers first think about how they’re going to approach a testing problem, then figure out what’s suitable for automation, and what’s not suitable.

    You should automate 100% of the tests that should be automated

    Alan’s heuristic when to automate: “I’m Bored”

    The coding tester

    Summary:

    • the role of a coder-tester is not to automate everything
    • testers do not need to have a computer science
    • testers do not need to be able to program
    • programming knowledge does not destroy “a proper tester angle”
    • background similar to customer’s does not make you a customer

    GUI shmooey

    For 95% of all software applications, directly automating the GUI is a waste of time.

    For the record, I typed 99% above first, then chickened out.

    Design for GUI automation

    Alan’s main points for disliking GUI automation:

    • It’s (typically) fragile — tests tend to break / stop working / work unsuccessfully often
    • It rarely lasts through multiple versions of a project (another aspect of fragility)
    • It’s freakin’ hard to automate UI (and keep track of state, verify, etc.)
    • Available tools are weak to moderate (this is arguable, depending on what you want to do with the tools — I’m particularly pleased, for example, with what good testers are able to do with selenium and web driver).

    I love GUI automation that can automatically explore variations of a GUI based task flow.

    I like GUI automation is in stress or performance issues.

    It’s (probably) a design problem

    • Record & Playback automation is a non-starter
    • Basic verification that would be hit by anyone walking through the basics of the application isn’t worth automation
    • Tests that do exactly the same thing every time are not valuable
    • Always think forward
    • Plan for failure and ensure that all test failures tell you exactly what is wrong
    • Tests should be reliable
    • There is always a better alternative to Sleep statements
    • UI is fragile, its testability should designed

    In the middle

    Alan’s brainstorming technique: first spend a reasonable amount of time focusing on the extremes — because often, some great ideas for “the middle” comes out of that brainstorming.

    Test design for automation

    The first step — and most important — is to think how you’re going to test.

    From that initial test design effort, you can deduce what aspects of testing could be accomplished more efficiently with automation (and without).

    Orchestrating test automation

    Designing good tests is one of the hardest tasks in software development.

    LOL — UR AUTOMASHUN SUCKZ!

    Your tests don’t suck:

    • when you treat their code like a production code
      • core reviews
      • static analysis
      • running with the debugger to ensure they are doing what you think they are
      • trust: if a test fails, it’s a product bug, not a test bug
    • when they execute automatically
    • when failures are handled automatically
      • bugs are entered automatically — including logs, call stacks, screen shots, trace information, and other relevant info
      • when bug is fixed, it’s checked automatically
      • generation of “Test Result Report”

    Musings on test design

    Some tests can only be run via some sort of test automation.

    Some tests can only be done via human interaction.

    You can’t effectively think about automated testing separately from human testing.

    In my world, there are no such things as automated testing, exploratory testing, manual testing, etc.

    There is only testing

    Beyond regression tests & testing with code

    Useful tests are tests that provide new information.

    An automation strategy that only performs regression testing is short-sighted and incomplete.

    How to make test useful:

    • model-based testing
    • introducing some randomness
    • data driven testing
    • scaled fault injection
    • fuzzing

    More on test design

    Test Design ideas are endless.

    To be a good test designer (and tester), you need a lot of testing ideas, and you need to know how and when to apply them.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2017/03/19/enabling-l2tpipsec-in-ubuntu/index.html b/articles/2017/03/19/enabling-l2tpipsec-in-ubuntu/index.html index e0f4e923..a978e24a 100644 --- a/articles/2017/03/19/enabling-l2tpipsec-in-ubuntu/index.html +++ b/articles/2017/03/19/enabling-l2tpipsec-in-ubuntu/index.html @@ -1,12 +1,12 @@ Enabling L2TP/IPSec in Ubuntu | aviskase

    Enabling L2TP/IPSec in Ubuntu

    +19 Mar 2017
    1 min

    Linux is like that: you can do anything, but sometimes it’s not easy for a common user. As for me, I hate writing config files for VPN, because network-manager is awesome. But sometimes it’s not easy to make it work.

    The biggest problem for me is LT2PT/IPSec. There is an excellent article on how to enable it using network-manager-l2tp. But as on-line articles have a tendency to be removed, I want to save these instructions here.


    Install the prerequisites:

    sudo apt install \  
     intltool \  
     libtool \  
    @@ -46,6 +46,6 @@
     cd xl2tpd-1.3.6  
     make  
     sudo make install  
    -

    Restart your machine, profit.

    Restart your machine, profit.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2018/01/06/2018-and-mega-mind-map/index.html b/articles/2018/01/06/2018-and-mega-mind-map/index.html index 35c5fc72..c3971d16 100644 --- a/articles/2018/01/06/2018-and-mega-mind-map/index.html +++ b/articles/2018/01/06/2018-and-mega-mind-map/index.html @@ -1,12 +1,12 @@ 2018 and mega mind map | aviskase

    2018 and mega mind map

    +6 Jan 2018
    +1 min

    Well, it’s been a while. I don’t want to make so called “new year resolutions”, but it’s better to add a repeating task in the RTM to write something here xD

    2018 started shaky. I left my first real place of work — Quality Lab — Alma mater of testing. This decision was heartbreaking, yet expected. On the bright side, now I have time to condition my brain into the normal mode again: for the last 1.5 year I wasn’t productive in studying and reading.

    First step is my mega mind map. God bless Freeplane, it’s awesome. Actually, it was a bit ugly, but now it’s looking good ;) So, MMM. It’s versioned with git and here is a repo. I have an impressive goal of documenting all testing techniques and approaches. Hope, I won’t drop it as usual.

    Here is a first iteration:

    • ACC (Attributes / Components / Capabilities)
    • Decomposition
    • APV aka ДПЗ (Actions / Parameters / Values)
    • Value analysis

    Mega mind map version 1

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2018/01/06/2018-and-mega-mind-map/mmm_v1_hu658e96a9d946f60ef47e18e9848f827a_277794_1603x2402_resize_q75_h2_box.webp b/articles/2018/01/06/2018-and-mega-mind-map/mmm_v1_hu3749709794121507567.webp similarity index 100% rename from articles/2018/01/06/2018-and-mega-mind-map/mmm_v1_hu658e96a9d946f60ef47e18e9848f827a_277794_1603x2402_resize_q75_h2_box.webp rename to articles/2018/01/06/2018-and-mega-mind-map/mmm_v1_hu3749709794121507567.webp diff --git a/articles/2018/01/23/testers-in-this-world/index.html b/articles/2018/01/23/testers-in-this-world/index.html index a0199887..c42aca43 100644 --- a/articles/2018/01/23/testers-in-this-world/index.html +++ b/articles/2018/01/23/testers-in-this-world/index.html @@ -1,9 +1,9 @@ Testers in this world | aviskase -

    Testers in this world

    +23 Jan 2018
    +3 mins

    Every tester saw those articles: “Testing is dead”, “Manual testing is dead”, “Testing is not dead”, “Automation is not testing”, “Company XXX has no testers and is happy about it”, etc. They might get on nerves. I love my craft, but sometimes something is nibbling at the back of my mind. Something that keeps me wondering: maybe I should move to a different role? So, I unscrambled this “something” and found an explanation why these thoughts are there and why I won’t leave testing =)

    Disclaimer: these thoughts are mine and I don’t have a goal to promote or impose them on other people. As a normal human being I do understand that my opinion may change with new experience and knowledge. This article contains a documented reflection on my experience so far. Probably it’ll be fun to read in 10-20 years (if I’ll still be a tester) xD


    Ideal world of software development

    Imagine an ideal world of software development. Scope is clear. Everything is done on time in relaxed manner without rush. Everybody is motivated to create the best product. Everybody has happy life outside of work. No stress. Users are eager to help too. And all these aren’t just for one product, but true for all software in the world. Great, isn’t it?

    Now, when I’m thinking about this ideal world, I can’t find a place for testers in it. There is just no place for bugs. Developers and analysts have all the time to design and build a product without real bugs. Remember, they don’t have just a time, they’re also motivated, so they definitively test product. They do it themselves… so there is no need for a separate role of a tester.

    Let’s visualize this tester-less world as a perfect disk:

    Ideal world of development

    Real world of software development

    But our current world is not ideal. No one has enough time. Overtimes. Burning out. Problems outside work. Toxic environment. No motivation. Some rogue manager keeps adding features out of scope. And bugs, bugs everywhere. This world is distorted, some products are a bit better than others, but no one is perfect.

    Real world of development

    Testers in the real world of software development

    And that’s where testers are coming. We are like clay, like sealing foam. We patch this not-ideal world. We make it less distorted.

    Real world of development with testers

    All products are distorted in different ways, that’s why you can see testers doing all kind of things. Some are just “manual monkeys.” On a different extreme are those who automate test cases that are written by others. Most are somewhere in between. There are projects where testers have a hat of analytic. Or support. Or both. Or PM. Someone, maybe gurus, don’t test at all: they mentor a team to test themselves, control that quality is efficiently insured by others.

    Here is a fun fact. That patched version is still a lie. Testers are also not perfect. Thus, our world looks more like this:

    Even more real world of development with testers

    We can’t patch all holes. But we are here to try.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2018/01/23/testers-in-this-world/world_1_hue891f741282783cff478f4964b8fe7a4_17195_302x302_resize_q75_h2_box_3.webp b/articles/2018/01/23/testers-in-this-world/world_1_hu6233766376807086096.webp similarity index 100% rename from articles/2018/01/23/testers-in-this-world/world_1_hue891f741282783cff478f4964b8fe7a4_17195_302x302_resize_q75_h2_box_3.webp rename to articles/2018/01/23/testers-in-this-world/world_1_hu6233766376807086096.webp diff --git a/articles/2018/01/23/testers-in-this-world/world_2_hu52b7143d2443b37c0f18ce7b9fa22928_15081_295x295_resize_q75_h2_box_3.webp b/articles/2018/01/23/testers-in-this-world/world_2_hu8558978813632460801.webp similarity index 100% rename from articles/2018/01/23/testers-in-this-world/world_2_hu52b7143d2443b37c0f18ce7b9fa22928_15081_295x295_resize_q75_h2_box_3.webp rename to articles/2018/01/23/testers-in-this-world/world_2_hu8558978813632460801.webp diff --git a/articles/2018/01/23/testers-in-this-world/world_3_hu8b77470e9ad8e254e810cd0179da3a1e_32421_413x302_resize_q75_h2_box_3.webp b/articles/2018/01/23/testers-in-this-world/world_3_hu8633935464993824570.webp similarity index 100% rename from articles/2018/01/23/testers-in-this-world/world_3_hu8b77470e9ad8e254e810cd0179da3a1e_32421_413x302_resize_q75_h2_box_3.webp rename to articles/2018/01/23/testers-in-this-world/world_3_hu8633935464993824570.webp diff --git a/articles/2018/01/23/testers-in-this-world/world_4_hu3a317744b05fce60f4bc9761dc3eaca5_28895_326x302_resize_q75_h2_box_3.webp b/articles/2018/01/23/testers-in-this-world/world_4_hu5770297687279124679.webp similarity index 100% rename from articles/2018/01/23/testers-in-this-world/world_4_hu3a317744b05fce60f4bc9761dc3eaca5_28895_326x302_resize_q75_h2_box_3.webp rename to articles/2018/01/23/testers-in-this-world/world_4_hu5770297687279124679.webp diff --git a/articles/2018/03/19/mega-mind-map-version-2/index.html b/articles/2018/03/19/mega-mind-map-version-2/index.html index 76b11c8d..42cceca0 100644 --- a/articles/2018/03/19/mega-mind-map-version-2/index.html +++ b/articles/2018/03/19/mega-mind-map-version-2/index.html @@ -1,9 +1,39 @@ Mega mind map: version #2 | aviskase -

    Mega mind map: version #2

    +19 Mar 2018
    +1 min

    My not-so-mega mind map has grown a little bit. I finally added exploratory testing tours and ideas on how to make test cases less rigid, all that great stuff from “Exploratory Software Testing” by James A. Whittaker.

    Mega mind map version 2

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2018/03/19/mega-mind-map-version-2/mmm_v2_hu116af588c1f8e9dc66e665512d81e3f1_804916_1182x2858_resize_q75_h2_box_3.webp b/articles/2018/03/19/mega-mind-map-version-2/mmm_v2_hu17247444718598669219.webp similarity index 100% rename from articles/2018/03/19/mega-mind-map-version-2/mmm_v2_hu116af588c1f8e9dc66e665512d81e3f1_804916_1182x2858_resize_q75_h2_box_3.webp rename to articles/2018/03/19/mega-mind-map-version-2/mmm_v2_hu17247444718598669219.webp diff --git a/articles/2018/03/20/remember-the-milk-for-task-management/index.html b/articles/2018/03/20/remember-the-milk-for-task-management/index.html index 3e16405e..92fca807 100644 --- a/articles/2018/03/20/remember-the-milk-for-task-management/index.html +++ b/articles/2018/03/20/remember-the-milk-for-task-management/index.html @@ -1,12 +1,12 @@ Remember The Milk for task management | aviskase

    Remember The Milk for task management

    +20 Mar 2018
    +1 min

    Today I received a letter from Remember The Milk that I had won a free year of Pro. That’s great, yet I feel a bit sad because somehow everyone talks about every other todo app and not RTM.

    I’ve been using RTM for several years. First, with free account, later with Pro. And it’s a second time I won a Pro :) RTM is great. For me, four killer features are:

    • Start dates and due dates
    • Very customizable repeat options
    • Really smart add syntax (you can input everything as one-liner and all task fields will be populated)
    • Smart lists (basically, it’s saved searches with operators like tag: and logic control with AND/OR/NOT/parentheses)

    You can implement any imaginable task management system with it. I use a setup based on M. Dorofeev’s approach. Of course, specialized apps are probably a bit easier if you strictly follow rules of one “true” system, but with RTM you can do the heck you want any time you want. There is a bizarre development fashion to “box” users with constraints, to give no options. And for me RTM is a breeze of sane air of freedom.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/07/30/amazing-marvin-for-task-management/index.html b/articles/2019/07/30/amazing-marvin-for-task-management/index.html index 28e3b99a..2e749c21 100644 --- a/articles/2019/07/30/amazing-marvin-for-task-management/index.html +++ b/articles/2019/07/30/amazing-marvin-for-task-management/index.html @@ -1,12 +1,12 @@ Amazing Marvin for task management | aviskase

    Amazing Marvin for task management

    +30 Jul 2019
    +10 mins

    I’m an infrequent blogger, so how weird it is that my last post was about RTM? I was a loyal RTM user for quite a time… Well, I have a new love now. His name is Marvin.

    Marvin is still young, yet powerful. He has some problems and rough corners, but development couple (yes, just 2 persons!) is the most responsive and creative force I’ve seen. I’m still learning and tweaking my system there, but I want to describe the first working iteration to be able to improve and compare later.

    Enabled strategies

    Strategies are like extensions: add more to have more abilities.

    Essential

    Because these are essential, I’ll explain their usage later.

    • Category Context (big square, also beneath title, show full path)

    • Task Notes

    • Labels

    • Timers

    • Backburner (no setup)

    • Planning Ahead

    • Smart Lists

    • Custom Sidebar

    • Top Mini List

    • Custom Sections

    • Dependencies

    • End Dates (show end dates below the task)

    • Start Dates

    • Work Session Scheduler

    • Saved Items (Templates)

    • Smart List Day Alerts

    • Auto-schedule Due Tasks (cutoff = 1 day)

    • Staleness Warning (period = 40 days)

    • Email to Marvin

    • Zapier Integration

    • Review Date

    • Weekly Review

    Extras

    • Eat that Frog (just 2 frog levels) — nice to have, but I don’t use it as intended (I tend to assign a frog to the bad tasks, but I don’t do them first)

    • Task Reminders (create automatically) — very important feature, but less powerful than in RTM for now (cannot create multiple reminders). Though, I have a feeling that with proper review system I actually don’t need reminders that much now.

    • Duration Estimates — I’m experimenting with having estimates for all tasks, but so far don’t feel it increases productivity.

    • Time Tracking (show > in title) — goes hand in hand with duration estimates, feels needed but not essential

    • Beat The Clock — same as time tracking, still experimenting with it

    • Project Focus Picker — just started to use it; at least it works as an “eye bugger” to push me to work on project

    • Suggested Task — never used it really, but something about it feels good ><

    • The Wall — using it occasionally, would like to have block division by section

    • Day Progress Bar — I don’t know why I enabled it

    • Procrastination Count (default) — important but not essential

    • Missing Next Steps Warning — important, but not very used much at the moment

    • Day Note (with archive) — nice to have, but I’m not very good at keeping a habit to write (as can be guessed by this blog updates frequency)

    • Calendar, Calendar Sync, All-Day Items, Top Mini List — I’ll have a whole calendar workflow moved to AM as soon as these will work with Google Calendar and Outlook. Until then, I have to go to calendars.

    • Dashboard — I like it, but not sure that I need it

    • Reward Tasks — awesome feature which I’ve never used. Dunno why.

    Planning and scheduling cycle

    My PS cycle has three phases:

    • Monthly planning

    • Weekly planning

    • Daily scheduling

    Notice difference between planning and scheduling? This is because AM has a bit head-scratching at first, but really powerful distinction between these processes. In short, planning is about assigning start date and end date (“soft deadline”) and scheduling is about assigning a do date (“when should I do this task”). Also, there can be a due date, it’s not quite clear is it planning or scheduling category. I think both, because I use Auto-schedule Due Tasks strategy. For example, if something is due tomorrow, this task will have a do date = today.

    Monthly planning

    I have a recurring task Plan tasks for the next month, which is setup to run monthly on the 31st day. To complete this task I go to Planning > Monthly and plan tasks only for the next month while working from Master List. I don’t want to overplan too much into the future. What can be changed is maybe I should work from some smart list, but so far I don’t have too many tasks in ML.

    Weekly planning

    This is a Weekly Review strategy with checklist scheduled to be done on Sundays. Checklist is a combination of weekly planning and everyday review stuff.

    Reformulate task names left in today pool

    Done on: Daily view

    This is a part from Jedi techniques, which goal is to rename tasks you didn’t complete for some reason. That way the next day they will look “fresher” or more inviting to you.

    Review calendars for 2 weeks ahead: add tasks if needed

    Done on: external sites, tasks are added to inbox

    • birthdays

    • special all day events like holidays

    • work meetings

    Basically, it’s something that is not really actionable, but has a day and duration. This is what I want to do in AM in the future, when calendar sync works better:

    • Smart list with all calendar events in the next 2 weeks (to use for weekly planning)

    • Top Mini List strategy showing upcoming birthdays in the next 3 days

    • Depending on how AM will show calendar events (probably as tasks which have to be completed, which is a bit unnecessary for me), maybe all of them should be shown in Top Mini List

    Right now what I use is a Custom Sidebar with links to my google and outlook calendars.

    Reflect on completed week: do I need to do more?

    Done on: Archive, tasks are added to inbox

    Go to Archive and check what was done this week. It’s a bit cumbersome, because it shows tasks per month and not per week. In RTM I’ve used a smart list for that, but AM does not support searching completed tasks (yet).

    Other way is to click through daily views, but for me it’s too many clicks)

    Review start dates for backburner tasks (smartlist)

    Done one: Master List, work from smartlist

    Smartlist: any start date, on backburner

    I use backburner only for tasks which are dependent on others or with start date in the future. I try to be very strict with start dates and set them only if it really makes sense, for example: getting a vaccine boost shot in Dec 2028 has a start date in Sept 2028, because I don’t want this task to pollute my planning all these years. Another use for start dates is for sub-project, like 3-day long learning session, which is part of a project without start date (because I want to do some preparatory tasks before session starts).

    So, in order to keep backburner in check, I review it once in week. Now that I think about it, maybe I should have an alert about tasks which does not have start date and are not dependent, but are in backburner…? But more on alerts later =)

    Review all projects: add new tasks if needed (smartlist)

    Done on: Master List, work from smartlist

    Smartlist: Projects

    I had to create a smart list to show all projects, because I wanted to see backburner’s too.

    Recall this day: write down everything missed (triggers/ backwards-day-recall)

    Done on: Sidebar, tasks added to inbox

    Two links in Sidebar:

    • link to mind map containing triggers

    • link to timer set for 20m

    Triggers are things which can be used to recall what was forgotten. For example, one of the subtrees in my mindmap contains all types of utilities or all kinds of cleaning which could be done.

    Backwards day recall is a technique also used to recall things. You sit down and try to remember today in detail in backwards: from now to the morning.

    Empty inboxes: paper, GMail, Outlook, Joplin, AM

    Categorize everything and clean up all inboxes I can have. First go with paper notes, then emails (gmail + outlook), then note taking application (for now it’s Joplin), and finally AM category Inbox

    If you see something that should be planned for this month, set it right away.

    Plan tasks for the next week (selecting from this month)

    Done on: Planning > Weekly, working from This month list

    Because everything was planned for this month, I can just bring relevant tasks to next week.

    Schedule tasks for Monday by checking next week list

    Done on: Daily view for tomorrow, working from This week list

    Daily scheduling

    Because there is no “Daily review” strategy yet, I have a recurring task for that. It repeats every Wed and Fri, just because I’m still getting accustomed to always do it. When I’m ready, it will be repeated every day except for Sunday, where weekly review is done.

    Checklist is a subset of weekly review:

    • reformulate

    • reflect on Completed Today

    • recall this day

    • empty inboxes

    • schedule tasks for the next day

    Extras

    Categories

    Main:

    • Inbox

    • Work

    • Household — tasks related to house or family

    • Hobbies — anything related to my hobbies, learning, and reading

    • Reputation — quite new for me, this is for tasks related to my external image. Participating in open source projects, buying birthday gifts, writing blog, answering some emails. Sometimes there is no clear distinction between hobbies and this category, so it’s fluid.

    • Health

    • Productivity — tasks like everyday review or cleaning up overflown inboxes. I suppose calendar sync will go there too.

    Sections

    I use Custom Sections strategies:

    • Morning

    • Work — linked to smartlist which finds all tasks/projects in #Work

    • Outside — linked to smartlist which finds all tasks/projects which has @outside label

    • Evening

    • Bonus

    Morning and Evening tasks are essentials, while Bonus ones are nice to do. Outside tasks are for stuff where I need to go somewhere, like shopping errands. I’m still not hard set on these categories, except for Work, this one will definitely stay.

    Alerts

    I use Smart List Day Alerts strategy for finding and fixing potential planning problems.

    • New items pulled from backburner — reminder to check items with *new

    • Stale — review items with *stale

    • To review (waiting or pinged) — some tasks are ready to review)

    • This week unestimated — add estimation for all tasks, smartlist: Tasks, no time estimate, &thisWeek scheduleDate today == ||

    Review

    Review date strategy is not the best name for my usage. I use it for tasks which are not done by me.

    • tag waiting (3 day) — for long waiting tasks

    • tag ping (1 day) — reminds me ask someone everyday if s/he finished the task

    Occasional tasks

    I was not able to setup this correctly in AM yet, so I’m using some hacks around it. Basically, these are tasks which I want to do every 15-40 days, without specifying exact day. One of the tasks is Productivity system review. It has a note with questions which I ask while going through all my tasks and projects:

    1. Is it really mine? Maybe delegate?

    2. Is there any real profit from this task?

    3. Maybe it’s possible to do some other task so that this one becomes obsolete?

    4. Is there any easier way to do it?

    5. Do I really still need to do it?

    Goal is to remove or reformulate tasks.

    Ending thoughts

    Of course this is just a small part of AM experience. I like being able to create work sessions for working on projects in pomodorro-style chunks. Templates are awesome and I use them for mindful book reading projects (reading, making notes, transferring them to Joplin). Gamification abilities are cute and I will explore them more, when I’ll be more comfortable and less procrastinating.

    I’ve noticed that features which were very important to me in RTM, like tagging, are not so needed here. Here you can have categories, sections, do/due/end dates, projects to achieve similar goals. This granularity and specificity are the most awesome aspect of AM!

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/08/08/lasha-tumbai-or-rm-rf-ru/index.html b/articles/2019/08/08/lasha-tumbai-or-rm-rf-ru/index.html index 34e21856..f8e3c39a 100644 --- a/articles/2019/08/08/lasha-tumbai-or-rm-rf-ru/index.html +++ b/articles/2019/08/08/lasha-tumbai-or-rm-rf-ru/index.html @@ -1,9 +1,9 @@ Lasha Tumbai or rm -rf RU | aviskase -

    Lasha Tumbai or rm -rf RU

    +8 Aug 2019
    +1 min

    Just a quick note: I’ve switched to Pelican site generator, because ruby-shuby decided not to work. Unfortunately, switching and preserving multi-language support is cumbersome. So, I’ve decided to ditch RU version. For what it’s worth, I moved to Canada and no longer really interested in trying to “promote” myself in Russian speaking communities.

    Yeah, yeah, “promote”, — written by a person who hasn’t write anything for ages. Right.

    Whoever read this blog in Russian, sorry. And, actually, I know that there were more traffic from CIS than from any other region >< But, two languages makes it too complicated and procrastinating to write in consistent manner (hysterical laugh).

    Зыс ис зе энд.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/08/26/revision-testers-in-this-world/index.html b/articles/2019/08/26/revision-testers-in-this-world/index.html index 2e9928dd..aab3c132 100644 --- a/articles/2019/08/26/revision-testers-in-this-world/index.html +++ b/articles/2019/08/26/revision-testers-in-this-world/index.html @@ -1,13 +1,19 @@ Revision: testers in this world | aviskase -

    Revision: testers in this world

    26 Aug 2019 -(upd: 17 Sep 2019)
    -3 mins

    Recently I’ve remembered my old article and wondered, how much my thoughts have changed and how they align to the principles of context-driven and modern testing (and yes, I don’t see them as contradicting each other).

    Remark in the parentheses is a bit stupid. It was discussed in the AB Testing episode 94 and at least for Alan and Brent there is no real contradiction.

    It looks like there are two main themes in that article.

    Theme one: finding a job as a tester

    A bit of a background: when I was writing that piece, I was in depression. That’s a different story about development culture and its effect on one’s mind, but I’ll write about it sometime later. So, depression. And a bit of worrying about getting the first job locally in Canada.

    No wonder that rereading the article now feel like the main takeaway was “software development is a shitty business, whatever progress will be made, there will be still more than enough companies to hire testers.” And I won’t argue the last part. Modern testing principles are things to strive for, but my experience so far tells me that it’s too optimistic to assume that all companies will end up there. So, there will be enough companies who will lag behind. Ergo, there will be enough positions for any testers. Even for mindless test case executors.

    Now, the question is, do I want to work in such companies? No.

    If I would want to continue as a tester-generalist, without specializing in some future-proof niches like pentest or perftest (at least, they seem to be future-proof), a sweet spot would be companies closer on a way to the “tester-less” transformation, but still needing some help on that way. Will there be enough such companies? Probably, yes. Getting position will be definitely harder but not impossible.

    Theme two: clay metaphor

    Nowadays I agree even more with the metaphor I came up with in that article. Yes, we are a clay or a sealing foam. We, testers, should help others in improving quality. That means that our work is immensely context-dependent: different organizations need different “holes” to seal.

    But we can go on even further with this metaphor: make it’s not just a clay, but a sci-fi fancy-shmancy clay which heals holes and scabs when applied. Yup, that means that after some time we won’t be needed in this organization; that’s fine. You can move to some other position where your experience being adaptable and generalizing could be of assistance. Or you can find other organization and kill testing position there too.


    As a result, while some wording was off, core principles still sound ok to me: adapt, help&heal, move on.

    older +(upd: 17 Sep 2019)
    +3 mins

    Recently I’ve remembered my old article and wondered, how much my thoughts have changed and how they align to the principles of context-driven and modern testing (and yes, I don’t see them as contradicting each other).

    Remark in the parentheses is a bit stupid. It was discussed in the AB Testing episode 94 and at least for Alan and Brent there is no real contradiction.

    It looks like there are two main themes in that article.

    Theme one: finding a job as a tester

    A bit of a background: when I was writing that piece, I was in depression. That’s a different story about development culture and its effect on one’s mind, but I’ll write about it sometime later. So, depression. And a bit of worrying about getting the first job locally in Canada.

    No wonder that rereading the article now feel like the main takeaway was “software development is a shitty business, whatever progress will be made, there will be still more than enough companies to hire testers.” And I won’t argue the last part. Modern testing principles are things to strive for, but my experience so far tells me that it’s too optimistic to assume that all companies will end up there. So, there will be enough companies who will lag behind. Ergo, there will be enough positions for any testers. Even for mindless test case executors.

    Now, the question is, do I want to work in such companies? No.

    If I would want to continue as a tester-generalist, without specializing in some future-proof niches like pentest or perftest (at least, they seem to be future-proof), a sweet spot would be companies closer on a way to the “tester-less” transformation, but still needing some help on that way. Will there be enough such companies? Probably, yes. Getting position will be definitely harder but not impossible.

    Theme two: clay metaphor

    Nowadays I agree even more with the metaphor I came up with in that article. Yes, we are a clay or a sealing foam. We, testers, should help others in improving quality. That means that our work is immensely context-dependent: different organizations need different “holes” to seal.

    But we can go on even further with this metaphor: make it’s not just a clay, but a sci-fi fancy-shmancy clay which heals holes and scabs when applied. Yup, that means that after some time we won’t be needed in this organization; that’s fine. You can move to some other position where your experience being adaptable and generalizing could be of assistance. Or you can find other organization and kill testing position there too.


    As a result, while some wording was off, core principles still sound ok to me: adapt, help&heal, move on.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/09/02/your-api-is-your-public-image/api_status_hufdb7540244ef7780496cfbba1ed5434c_101541_616x279_resize_q75_h2_box_3.webp b/articles/2019/09/02/your-api-is-your-public-image/api_status_hu11971910985276672202.webp similarity index 100% rename from articles/2019/09/02/your-api-is-your-public-image/api_status_hufdb7540244ef7780496cfbba1ed5434c_101541_616x279_resize_q75_h2_box_3.webp rename to articles/2019/09/02/your-api-is-your-public-image/api_status_hu11971910985276672202.webp diff --git a/articles/2019/09/02/your-api-is-your-public-image/bad_error_message_hu81ce616e61ffc978cfe25a90f3ebba43_24613_565x241_resize_q75_h2_box_3.webp b/articles/2019/09/02/your-api-is-your-public-image/bad_error_message_hu9065905591248337430.webp similarity index 100% rename from articles/2019/09/02/your-api-is-your-public-image/bad_error_message_hu81ce616e61ffc978cfe25a90f3ebba43_24613_565x241_resize_q75_h2_box_3.webp rename to articles/2019/09/02/your-api-is-your-public-image/bad_error_message_hu9065905591248337430.webp diff --git a/articles/2019/09/02/your-api-is-your-public-image/index.html b/articles/2019/09/02/your-api-is-your-public-image/index.html index 3796da10..e3ff3a15 100644 --- a/articles/2019/09/02/your-api-is-your-public-image/index.html +++ b/articles/2019/09/02/your-api-is-your-public-image/index.html @@ -1,9 +1,9 @@ Your API is your public image | aviskase -

    Your API is your public image

    +2 Sep 2019
    +7 mins

    Disclaimer: this is a translation of the article written 2 years ago for a corporate blog. I didn’t do a word-for-word translation because the original article went through an editor, whose style was not that close to mine. Too watered down and “official.” Also, some examples don’t make sense in English. Still, I didn’t update it too radically. Bear in mind, at the moment of the writing I was testing SOAP services and Excel-based import/export at big government project, so most of the examples relate to that experience.

    Sometimes you’ll see a block like that. It will contain my current thoughts on the subject or comments.


    First, what is API?

    API (Application Programming Interface) is an interface which helps apps to communicate with each other. Just as a human interacts with apps via buttons and dialogs (user interface, UI), so apps interact via APIs.

    Types of API

    One way to define API types is whether it’s public or private API. Private API is used for interactions inside your system, for example:

    • sync between mobile or desktop app and a server
    • app uses server’s computational resources (e.g., an image stylization app sends image and selected style to the server, where stylization will be done)
    • communications between a web app and server
    • communications between micro-services

    Primary risks for private APIs are functional and performance problems. Here, customers can only speculate why app works somehow wrong-y.

    With public API communication endpoints go beyond your system boundaries. Either you use someone’s API (social networks, maps, etc.) or you provide your API to external developers.

    Let’s talk about public APIs a bit more. For some companies providing APIs is the core business (e.g., payment processing: Stripe, Rebilly), for others it’s just a nice to have addition to the main services. Whatever the case, public APIs open a perspective into how your internal development process works. And you won’t be able to hide behind fancy UI and eloquent support team.

    If you publish public API with bad documentation, versioning mess, and tons of functional issues, make no mistake, external developer can (and maybe should!) assume that all your system is developed is such manner. Will they build their services around such system and attract new users? Nope. Will they persuade their bosses and friends against using and/or buying your product? Probably. Don’t forget that people who are not so tech-y value developers’ opinions a lot. And of course, those developers could also give your a bad reputation by complaining on social networks or forums. Therefore, before publishing even the tiniest API, you should think about its quality.

    Four ways to fuck up a public API

    There are different techniques for assessing API quality (for example, hierarchy of needs). Let’s talk about four main ways to be an awful API:

    Broken functionality

    Sounds banal, but a service should work. And it should provide functionality it was created for. On one of my projects, there was an embarrassing situation with an export. We tested API with different objects under various conditions, but only with the small number of objects. All was fine until we found a bug on export with lots of data. The thing is that the key purpose for this service was to provide an ability to do massive exports, therefore, the service didn’t fulfill its main reason for existence.

    You need to check available operations in context of other operations. For example, we released an import operation for objects A. It required an id of object B in the request body, but import and export of B was unreleased at the moment. As the result, it was impossible to do import A at all.

    Other possible problem: do you consider a region where API will be used? Obviously, support for Cyrillic is not that important for purely US oriented product. But if you work globally, do not forget to check non-ASCII characters! Even though Unicode seems to be default, I did find bugs like that one: a user uploaded a file with the name Документ_1.pdf, yet it was saved as _1.pdf.

    One more example. We had a service for chunked file download which “ate” last byte of the last chunk. It was highly critical problem because this service was a part of the system where these files were used as supporting documents for legal agreements.

    Unreliability

    Service is reliable if it works when it’s expected to be working and provides timely feedback in case of any problems.

    Worst performance problems I encountered were with export services. One of them was working perfectly fine until the biggest organization in the system started using it and crashing application servers. Hot-fixes after hot-fixes, optimizations, new version; nothing helped. And we couldn’t disable that service or completely rewrite its public API because of contractual obligations.

    So, what if your service experiences problems? How external users will learn about it? Will there be any alert about temporary issues or downtimes? Any resolution time frames? Usually, there is a special web page with answers to these question, with a table like this:

    API status table

    And beware, this page should not on the same infrastructure as the services it’s showing! It would be quite embarrassing if it goes down at the same time your services go down.

    Crappy usability

    When we hear the word “usability”, we usually think about GUI: buttons and dialogs. I think GUI usability is somewhat overrated: even in the ugliest app you can guess your way by trial and error. With API it won’t work:

    • No public documentation? Users will never even know that API exists.
    • Public documentation is there, but there is no info about actual endpoints? Users won’t be able to call API.
    • Public documentation is there, but written in such manner that without knowing internal docs you can’t understand a thing? Again, users won’t be happy at all.
    • Spelling mistakes? Not that critical if in text, but can be quite awful in schemes. Real support ticket:

    Your developers drink too much and it impairs their accuracy. There is an epic fail in a scheme with the name of the element Pressure: the first letter is a Cyrillic character and it breaks all client code generation.

    Cyrillic Р/р (pronounced like “r”) looks exactly like Latin P/p.

    • Service works fine, but error messages are not that informative? Users won’t understand how to fix an error (and probably will open a support ticket, so you’ll needlessly spend time resolving non-existing issue).

    Unhelpful error message

    • You have UI and API? Don’t forget to check they correspond to each other. The most common problem is when constraints on UI fields don’t match same fields in API: for example, UI can accept maximum 50 characters for the name and API only 20, which leads to errors when trying to export anything created on UI.

    • Don’t forget about versionning (in API and its documentation). The older your services are, more careful you should be with incompatible changes. Documentation should always be up-to-date: sounds obvious, but we had a big fuck up when someone accidentally published documentation for upcoming API version and external developers started trying to use these new features, didn’t find them, and bombed support team with “nothing works again” tickets.

    Security holes

    When you publish API you also increase a potential attack surface for hackers. First of all, think about authorization and authentication processes. Typically, there are special access tokens for API users. Maybe simple developers’ tokens will be enough for your case, maybe you’ll need to use flows like OAuth. In some cases you should sign requests and responses.

    Oftentimes there are several APIs: for example, test API (for internal developers and testers) and open public API. You should make sure that test API is secured enough. There are known cases when web crawlers accidentally found test endpoints and happily showed them in search results.

    If you provide access to test API to external developers, you’d better treat this API as high security risk. One time I found a stackoverflow question with code snippet containing authorization keys and proper endpoints for our system.

    One open source project I used had a different issue. Test API was used by developers to help with testing: add money to the account, change account status to premium, etc. It was hidden and secure… Until someone released a version with these APIs enabled in production. That’s bad :)

    Conclusion

    After reading all of these, you’d think that public APIs are too risky, challenging, and expensive. Perhaps, it’s better not to provide it? Maybe. But global connectivity is a trend. Stable and useful API can facilitate your profits: it can increase your user base via external apps or advertise your workplace to professionals. It’s a demanding work, but it pays off.

    And even if you don’t and won’t have public API, think about your private ones. We should care about your own developers, shouldn’t we?

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/09/04/stereotype-rant/index.html b/articles/2019/09/04/stereotype-rant/index.html index c1cd4a11..a358a2eb 100644 --- a/articles/2019/09/04/stereotype-rant/index.html +++ b/articles/2019/09/04/stereotype-rant/index.html @@ -1,9 +1,9 @@ Stereotype Rant | aviskase -

    Stereotype Rant

    +4 Sep 2019
    +2 mins

    We live in the world of memes and funny pictures. Nothing wrong with that. Some companies started posting humorous content too, and, again, that’s totally fine: I myself prefer these to dry corporate pitches. But it’s a real shame when you see a post like this from a test agency:

    What makes you feel more powerful:

    • Money
    • Status
    • Finding bugs in other people’s code

    It would be dishonest to say I would never find this joke amusing. I did. Probably first half a year of being a tester, less or more. And I still hear that “achievement unlocked ding” in my head when I find some peculiar or very critical issue.

    But I most certainly don’t feel powerful. Experience with software development comes with a grain of salt and a certain sadness. Each bug you discover means:

    • there are even more unknown problems lurking somewhere
    • you have to bother about code correctness instead of thinking about the value
    • processes are leaky and if I’m not in position to have any impact on them I’d probably feel powerless

    So, sorry, but no, that post won’t receive a “like” from me. And I’m quite angry and sad seeing such posts coming from test agency, company that shouldn’t be promoting stereotypes about testers as those guys who are happy to “break stuff” and are always “at war with devs.” C’mon! In the case of this particular company, I understand that it’s most probably marketing shit which wasn’t approved by actual testers working there. But often it happens that we, testers, either don’t care or not vocal enough about pointing out these harmful mistakes to juniors and non-dev people.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/09/08/using-google-apps-scripts-for-productivity-improvements/index.html b/articles/2019/09/08/using-google-apps-scripts-for-productivity-improvements/index.html index 5639e19b..14450a6e 100644 --- a/articles/2019/09/08/using-google-apps-scripts-for-productivity-improvements/index.html +++ b/articles/2019/09/08/using-google-apps-scripts-for-productivity-improvements/index.html @@ -1,14 +1,26 @@ Using Google Apps scripts for productivity improvements | aviskase

    Using Google Apps scripts for productivity improvements

    +8 Sep 2019
    2 mins

    Google Apps Scripts are probably the most useful automation tools I’ve used. They can be used as “excel macros” for Sheets, form processing, and much more. Here I want to share three small scripts I made to improve productivity and task management.

    Mark all emails as read

    If you ever got bothered by all archived and still unread emails in Gmail, this script can help you. It is based on the script by Mike Crittenden.

    I don’t really need it right now, because I have a filter which marks all incoming emails as read right away:

    Matches: larger:1
     Do this: Mark as read
    -

    But it was effective for cleaning up.

    Tasks recurring randomly for Amazing Marvin

    I use Amazing Marvin for task&project management and currently it doesn’t support randomly recurring items. In fact, no app I’ve tried supports that. It’s a shame, because there could be several use cases for that: “spontaneous” cleaning&organizing, fun activities, ideas review.

    If you are able to import tasks (for example, via email), you can check this script. The most important thing is a TASKS list. Each item should have range_start and range_end. For example, range_start = 2 and range_end = 9 mean that task will be created in ranges from two to nine days after last created date. E.g. if the last time task with this id was created on September 10, next task will be created sometime between September 12 and September 19.

    Script ensures the task will be created at some point during this range, just make sure it’s triggered to run daily.

    Create a task when new package release is available on PyPI

    I have a weird project which can start only after a particular release of one python package. It’s not very urgent, so no hurry, but I don’t want to check for releases manually.

    This script checks RSS for the package on libraries.io and if there is a new version available, it will send email to AM to create a task.


    I didn’t go into details about how to setup these scripts, so if you have any questions, feel free to comment.

    But it was effective for cleaning up.

    Tasks recurring randomly for Amazing Marvin

    I use Amazing Marvin for task&project management and currently it doesn’t support randomly recurring items. In fact, no app I’ve tried supports that. It’s a shame, because there could be several use cases for that: “spontaneous” cleaning&organizing, fun activities, ideas review.

    If you are able to import tasks (for example, via email), you can check this script. The most important thing is a TASKS list. Each item should have range_start and range_end. For example, range_start = 2 and range_end = 9 mean that task will be created in ranges from two to nine days after last created date. E.g. if the last time task with this id was created on September 10, next task will be created sometime between September 12 and September 19.

    Script ensures the task will be created at some point during this range, just make sure it’s triggered to run daily.

    Create a task when new package release is available on PyPI

    I have a weird project which can start only after a particular release of one python package. It’s not very urgent, so no hurry, but I don’t want to check for releases manually.

    This script checks RSS for the package on libraries.io and if there is a new version available, it will send email to AM to create a task.


    I didn’t go into details about how to setup these scripts, so if you have any questions, feel free to comment.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/09/15/soap-testing-101/index.html b/articles/2019/09/15/soap-testing-101/index.html index 6a16b342..8ef45390 100644 --- a/articles/2019/09/15/soap-testing-101/index.html +++ b/articles/2019/09/15/soap-testing-101/index.html @@ -1,10 +1,10 @@ SOAP testing 101 | aviskase -

    SOAP testing 101

    +15 Sep 2019
    +9 mins

    Disclaimer: this is a translation of the article written 2 years ago for a corporate blog. I didn’t do a word-for-word translation because the original article went through an editor, whose style was not that close to mine. Too watered down and “official.” Also, some examples don’t make sense in English. Still, I didn’t update it too radically. Bear in mind, at the moment of the writing I was testing SOAP services and Excel-based import/export at big government project, so most of the examples relate to that experience.

    Sometimes you’ll see a block like that. It will contain my current thoughts on the subject or comments.


    SOAP (Simple Object Access Protocol) is a standardized protocol for communication between a server and a client. Typically, it’s used over HTTP(S), but it can operate over other application level protocols like SMTP or FTP.

    Testing SOAP services is not drastically different from any other API testing, but you need to learn some specifics and use better suited tools. This article will provide a small checklist of know-hows and skills which you can use as a guide for getting started and improving your work.

    Theory

    SOAP is a protocol, so you need to read about the protocol itself as well as standards and other protocols it uses and, when the time comes, about its extensions.

    XML

    XML is a markup language similar to HTML. Every message sent via SOAP is a XML-document, where you can easily identify how data are structured.

    <?xml version="1.0"?>
     <note>
       <to>aviskase</to>
       <from>universe</from>
    @@ -46,8 +46,8 @@
       <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">YF6j8V/CAqi+1nRsGLRbuZhi</wsse:Nonce>
       <wsu:Created>2008-04-28T10:02:11Z</wsu:Created>
     </wsse:UsernameToken>
    -

    Extension can be complex and intimidating. You can skip learning about them in details if your service does not have them.

    Tools

    SOAP is a serious business: all this theory and standards. And XMLs tend to be huge. You can, but you don’t want to use tools like notepad and cURL for daily work. But don’t worry, there are tools to make life easier with SOAP.

    XML&XSD editors

    As I said before, you need to start testing with documentation. For that you can use special editors. Two most common are Oxygen (cross-platform) and Altova (Windows-only); both are not free. These are feature-heavy applications used by analysts.

    I routinely use three features: XSD visualization, XML generation, and XML validation.

    XSD visualization is helpful to easily spot schema elements, attributes, and restrictions. For example, CheckTextRequest has a required text element, whereas all three attributes are optional. In addition, attribute option has a default value 0.

    XSD visualization in Oxygen

    Visualization is handy when there are lots of types and restrictions. If you need only this feature and you don’t want to buy apps, you can check -free alternatives like JDeveloper.

    You can use XML generation to quickly create examples of messages based on supplied XSD. I use is for exploration and experimentation on possible XMLs and checking how restrictions are applied.

    After generating messages, you can perform XML validation on them. Together these two features allow you to find issues with a service even when it’s not yet implemented.

    Testing with SoapUI

    Testing SOAP services almost always means using SoapUI. You can read about it in this tutorial or the official documentation.

    I’ve come up with eight possible levels of SoapUI mastery.

    Level 1: sending requests

    Learn how to create a project based on WSDL. SoapUI can generate all requests for you; all you need to do is to check their correctness and click a “Send” button. After getting used to send valid requests you’ll need to start creating invalid ones to get expected error messages.

    Level 2: using “Test suites” and “Test cases”

    Start writing mini-autotests. Tests suites and test cases allow creating automation scenarios, prepare test data, and automatically assert responses. At first you can use them simply for organization. For example, if you filed an issue, you’d want to quickly check its fix and for that you can have a separate test suite with requests specific to each issue.

    Level 3: writing assertions

    After using cases for some time, you’ll want to make them automatically asserted to mark valid responses as green and invalid ones as red. SoapUI has lots of assertion types, but the simplest ones are Contains and Not Contains which you can use to check existence of some string in the response. These assertions also support regular expressions.

    Level 4: using XPath and/or XQuery in assertions

    XPath is a familiar thing for those who work on UI automation with Selenium. In short, XPath is used to search for elements inside XML-document. XQuery is a similar technology which can use XPath “inside”; this language is way more powerful and SQL-like. Both can be used in assertions and will make checks more robust and precise.

    Level 5: writing complex checks with special steps

    Test cases can contain several requests and other special steps, which can form a user flow (for example, a scenario “create entity → export entity”). Some of those steps are:

    • Properties and Property Transfer (help to reuse data and transfer it between requests)
    • JDBC Request (used for getting data from databases)
    • Conditional Goto (allows to create conditions and loops)
    • Run TestCase (help to reuse common test cases)

    Level 6: using Groovy scripts

    SoapUI supports writing scripts in Groovy language. The simplest idea is to generate data embedding Groovy via ${=} inside a request body. For example:

    • Insert current date and time: ${=new Date().format("yyyy-MM-dd'T'HH:mm:ss")}
    • Insert a random GUID: ${=java.util.UUID.randomUUID()}

    More complex scripts can be used in separate steps and assertions. At some point you’ll notice you tend to write everything inside script step instead of steps from level 5.

    Level 7: using MockServices

    SoapUI can generate mock objects based on WSDL. It’s a crude service simulation which you can use to write and debug test cases before they are actually available for testing. Also, you can use them in place of temporarily down services.

    Level 8: god-mode

    You recognize differences between free and pro versions of SoapUI. You use SoapUI as a library from code. You use plugins and run test cases via CLI and/or CI. Your tests cases are elegant and easy to support. You know all the angles! I envy you.

    Or not. Presently I don’t use any GUI tools for API testing except for exploratory sessions.

    Testing with code

    At some point you’ll have a feeling that it’s more efficient to use programming languages for writing tests. That’s fine. SoapUI is a GUI application and has its drawbacks; some of them are resolved with pro version, while others require huge and brittle scripting hacks.

    There are tons of libraries for working with SOAP in any language. For example, there are Axis2 for Java (good examples are in the articles at IBM developerWorks), suds or zeep for Python, or groovy-wslite for Groovy.

    Here is an example of request made to YandexSpeller API using groovy-wslite:

    import wslite.soap.*
    +

    Extension can be complex and intimidating. You can skip learning about them in details if your service does not have them.

    Tools

    SOAP is a serious business: all this theory and standards. And XMLs tend to be huge. You can, but you don’t want to use tools like notepad and cURL for daily work. But don’t worry, there are tools to make life easier with SOAP.

    XML&XSD editors

    As I said before, you need to start testing with documentation. For that you can use special editors. Two most common are Oxygen (cross-platform) and Altova (Windows-only); both are not free. These are feature-heavy applications used by analysts.

    I routinely use three features: XSD visualization, XML generation, and XML validation.

    XSD visualization is helpful to easily spot schema elements, attributes, and restrictions. For example, CheckTextRequest has a required text element, whereas all three attributes are optional. In addition, attribute option has a default value 0.

    XSD visualization in Oxygen

    Visualization is handy when there are lots of types and restrictions. If you need only this feature and you don’t want to buy apps, you can check +free alternatives like JDeveloper.

    You can use XML generation to quickly create examples of messages based on supplied XSD. I use is for exploration and experimentation on possible XMLs and checking how restrictions are applied.

    After generating messages, you can perform XML validation on them. Together these two features allow you to find issues with a service even when it’s not yet implemented.

    Testing with SoapUI

    Testing SOAP services almost always means using SoapUI. You can read about it in this tutorial or the official documentation.

    I’ve come up with eight possible levels of SoapUI mastery.

    Level 1: sending requests

    Learn how to create a project based on WSDL. SoapUI can generate all requests for you; all you need to do is to check their correctness and click a “Send” button. After getting used to send valid requests you’ll need to start creating invalid ones to get expected error messages.

    Level 2: using “Test suites” and “Test cases”

    Start writing mini-autotests. Tests suites and test cases allow creating automation scenarios, prepare test data, and automatically assert responses. At first you can use them simply for organization. For example, if you filed an issue, you’d want to quickly check its fix and for that you can have a separate test suite with requests specific to each issue.

    Level 3: writing assertions

    After using cases for some time, you’ll want to make them automatically asserted to mark valid responses as green and invalid ones as red. SoapUI has lots of assertion types, but the simplest ones are Contains and Not Contains which you can use to check existence of some string in the response. These assertions also support regular expressions.

    Level 4: using XPath and/or XQuery in assertions

    XPath is a familiar thing for those who work on UI automation with Selenium. In short, XPath is used to search for elements inside XML-document. XQuery is a similar technology which can use XPath “inside”; this language is way more powerful and SQL-like. Both can be used in assertions and will make checks more robust and precise.

    Level 5: writing complex checks with special steps

    Test cases can contain several requests and other special steps, which can form a user flow (for example, a scenario “create entity → export entity”). Some of those steps are:

    • Properties and Property Transfer (help to reuse data and transfer it between requests)
    • JDBC Request (used for getting data from databases)
    • Conditional Goto (allows to create conditions and loops)
    • Run TestCase (help to reuse common test cases)

    Level 6: using Groovy scripts

    SoapUI supports writing scripts in Groovy language. The simplest idea is to generate data embedding Groovy via ${=} inside a request body. For example:

    • Insert current date and time: ${=new Date().format("yyyy-MM-dd'T'HH:mm:ss")}
    • Insert a random GUID: ${=java.util.UUID.randomUUID()}

    More complex scripts can be used in separate steps and assertions. At some point you’ll notice you tend to write everything inside script step instead of steps from level 5.

    Level 7: using MockServices

    SoapUI can generate mock objects based on WSDL. It’s a crude service simulation which you can use to write and debug test cases before they are actually available for testing. Also, you can use them in place of temporarily down services.

    Level 8: god-mode

    You recognize differences between free and pro versions of SoapUI. You use SoapUI as a library from code. You use plugins and run test cases via CLI and/or CI. Your tests cases are elegant and easy to support. You know all the angles! I envy you.

    Or not. Presently I don’t use any GUI tools for API testing except for exploratory sessions.

    Testing with code

    At some point you’ll have a feeling that it’s more efficient to use programming languages for writing tests. That’s fine. SoapUI is a GUI application and has its drawbacks; some of them are resolved with pro version, while others require huge and brittle scripting hacks.

    There are tons of libraries for working with SOAP in any language. For example, there are Axis2 for Java (good examples are in the articles at IBM developerWorks), suds or zeep for Python, or groovy-wslite for Groovy.

    Here is an example of request made to YandexSpeller API using groovy-wslite:

    import wslite.soap.*
     def client = new SOAPClient('http://speller.yandex.net/services/spellservice?WSDL')
     def response = client.send(SOAPAction: 'http://speller.yandex.net/services/spellservice/checkText') {
       body {
    @@ -58,6 +58,6 @@
     }
     assert 'mistake' == response.CheckTextResponse.SpellResult.error.s.text()
     assert '1' == response.CheckTextResponse.SpellResult.error.@code.text()
    -

    As far as I know there are no high-level test frameworks for SOAP (similar to Rest-assured), but there is an interesting framework called karate where you can describe test cases for SOAP and REST in Cucumber / Gherkin style.

    I don’t do any Groovy programming anymore, so no idea how groovy-wslite fares. In fact, I don’t do SOAP testing now, so my knowledge about libraries can be outdated.

    Plus, I avoid anything cucumber-ish =)


    I doubt anyone will go and test SOAP services in their free time just of curiosity. It’s a heavy protocol used in serious corporate systems. But its heaviness is a good news for testing: all technologies are standardized and there is a vast ecosystem around it.

    So, here is a promised checklist. If you are only starting to test SOAP services, you need to learn and be able to use:

    • XML
    • XSD
    • WSDL
    • SOAP
    • XML&XSD editors for XSD visualization
    • SoapUI level 1

    Basically, just standards and being able to send requests, nothing more. That’s it. Of course, you’ll unavoidably learn more with time, but don’t deep dive right away or your head will explode =)

    As far as I know there are no high-level test frameworks for SOAP (similar to Rest-assured), but there is an interesting framework called karate where you can describe test cases for SOAP and REST in Cucumber / Gherkin style.

    I don’t do any Groovy programming anymore, so no idea how groovy-wslite fares. In fact, I don’t do SOAP testing now, so my knowledge about libraries can be outdated.

    Plus, I avoid anything cucumber-ish =)


    I doubt anyone will go and test SOAP services in their free time just of curiosity. It’s a heavy protocol used in serious corporate systems. But its heaviness is a good news for testing: all technologies are standardized and there is a vast ecosystem around it.

    So, here is a promised checklist. If you are only starting to test SOAP services, you need to learn and be able to use:

    Basically, just standards and being able to send requests, nothing more. That’s it. Of course, you’ll unavoidably learn more with time, but don’t deep dive right away or your head will explode =)

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/09/15/soap-testing-101/oxygen_huaa17ea1e9ca9d008b12aee7f1bd8511b_16509_391x284_resize_q75_h2_box_3.webp b/articles/2019/09/15/soap-testing-101/oxygen_hu10729633755816028478.webp similarity index 100% rename from articles/2019/09/15/soap-testing-101/oxygen_huaa17ea1e9ca9d008b12aee7f1bd8511b_16509_391x284_resize_q75_h2_box_3.webp rename to articles/2019/09/15/soap-testing-101/oxygen_hu10729633755816028478.webp diff --git a/articles/2019/09/24/new-domain/index.html b/articles/2019/09/24/new-domain/index.html index 6303e654..e979e7ab 100644 --- a/articles/2019/09/24/new-domain/index.html +++ b/articles/2019/09/24/new-domain/index.html @@ -2,11 +2,11 @@

    New domain

    +24 Sep 2019
    +1 min

    Yay, I’ve bought my own domain!

    First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went with simple aviskase.com. Not that I am particularly invested into SEO and stuff, but all the articles recommend to be boring. Also, I used comparable products heuristic: most of the blogs I am subscribed to have the same dull TLD.

    BTW, if anyone ever was subscribed to RSS, sorry for whole new regeneration =(

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/09/30/achievement-unlocked-rsta/index.html b/articles/2019/09/30/achievement-unlocked-rsta/index.html index 65c01e89..f10d590c 100644 --- a/articles/2019/09/30/achievement-unlocked-rsta/index.html +++ b/articles/2019/09/30/achievement-unlocked-rsta/index.html @@ -1,9 +1,9 @@ Achievement unlocked: RSTA | aviskase -

    Achievement unlocked: RSTA

    +30 Sep 2019
    +3 mins

    Landscape of testing related courses is problematic. On one hand, there are lots of courses. On the other hand, there are few courses I would consider. Either they are lackluster and certificate-centered, or entry-level-only, or mind-bogglingly expensive. And about the last bit. Many say that if a company provide a training budget, costs don’t matter. Well, maybe if that company is one of FAANG, you can think so. But I work in a small one and I don’t want to throw its (our?) money away.

    Nevertheless, there are always some courses and trainings which you just want to attend to. For example, those from Satisfice, Inc. That’s why I applied as soon as I saw time convenient online version of Rapid Software Testing Applied. And, BTW, the price was decent.

    I don’t want to write about the course agenda and curriculum: google is your friend. I will neither sing praises to its appeal and importance nor criticize its materials. We can argue as much as we like on should testers be able to code or not, but the knowledge of who James Bach and Michael Bolton are can be a mark of competency (necessary but not sufficient). And based on this knowledge, I think, it’s quite obvious what you can expect from such course. I also won’t describe what I learned: the most useful attainments are tacit and will surface in future work and articles.

    So, this article is about technical moments. My previous place of work, Quality Lab, provides trainings, and from there I’ve discovered an interest in learning and comparing processes used in education.

    RSTA was held September 18-20, completely online. It was nice that we used Mattermost for communications; I used this open source messenger before. Usually, it’s always scary how courses handle linux users: sometimes they require skype (which became quite awful) or webinars are streamed with god knows what. Here everything was ok, linuxhead’s feelings were not offended.

    After the course we received all materials, not just slides and recordings, but also:

    • Agenda & log
    • Recordings
    • Class materials (like slides and articles to read)
    • Session reports with attachments (with comments by instructor)
    • Bug list (with comments and attachments)
    • Group chats archive

    Last part is really awesome. I, like a fool, sat and copied all important messages. Even woke up during the night realizing I forgot to save some PDFs. And it turned out not to be a problem at all, because I have the whole archive now. Super.

    One thing I would change is a duration: three days for “Applied” is too fast. You’d want more practice. For example, double all assignments, where the first time would be a “learning” and the second time — “reinforcement and revision”. Reporting assignments would be a great addition too. Also, it would be interesting to intensify students’ cooperations: working in teams was possible, but wasn’t required. What if there was one obligatory assignment for paired testing?..

    Our group, as I understand, was smaller than usual. But for me it was an upside, because I read all assignments and bug reports. As usual, some students were more active than others: big shoutout for them for questions and discussions!

    Overall atmosphere was pleasant. I noticed that in some other courses instructors were present only as talking heads in the pre-recorded videos and names in the ads. Not in this case. James answered questions himself and commented on assignments and bugs; peers advisors only helped.

    Active students, honest instructor and peer advisors are the most significant qualities for me. We go to the training to get out from our bubble; the more communications and sharing we get, the more valuable is this experience. And RSTA definitely fulfilled this expectation.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/10/05/internal-struggle-with-language-gymnastics/index.html b/articles/2019/10/05/internal-struggle-with-language-gymnastics/index.html index 10039642..94a10f49 100644 --- a/articles/2019/10/05/internal-struggle-with-language-gymnastics/index.html +++ b/articles/2019/10/05/internal-struggle-with-language-gymnastics/index.html @@ -1,15 +1,18 @@ Internal struggle with language gymnastics | aviskase

    Internal struggle with language gymnastics

    +5 Oct 2019
    +5 mins

    During the RSTA class I asked a question about language and communications:

    On one hand, I wholly agree with the notion that we should be attentive with words. Like checking vs testing, “quality assurance”, and all those other things. I find myself as they say “nit-picking” quite often.

    On the other hand, I also follow a rule “if your team understands you, words don’t matter”. Especially, if you come to the established project with existing vocabulary. It can be bizarre and absurd (my favorite was “bug validation” for the process of checking bug fixes), but it’s habitual to everyone, so you’ll likely spend more time trying to “reteach” than working. And you’ll probably fail.

    So… how to live with this contradiction?

    And later:

    As I understand you, contradiction is resolved by considering how much harm it can create?

    That makes sense. Pain and risk analysis in practice. Even though I said that “defect validation” absurdity was my favorite, it wasn’t harmful for thinking processes: we usually didn’t even call it like that and used abbreviation “DV”. Or sometimes QA (grrr). I noticed that majority of non-dev colleagues don’t even know what A stands for. Probably it’s the reason why some testers come up with new reinterpretations like “Advocate” or “Assistant”. BTW, here is a funny example.

    While James debated that there is no contradiction, it is there. It’s my internal contradiction. I feel these sides of me constantly struggling; for an outside viewer it can be perceived as inconsistency.

    I haven’t found resolution to this conflict yet, but it occurred to me that it is based on the conflict between characteristic testers’ nit-pickiness and my amateur linguistics studies.

    You see, when it comes to languages in general, my position is 100% on the side of “all is fine as long we understand each other”. Why?

    Pronunciation

    I’m not English native speaker. And I’m not gifted with an ability to perfectly emulate a native pronunciation. In fact, sometimes I have to speak with an even heavier Russian accent than my usual, because it’s easier to understand for some people.

    Now, if we go even further, what is a native pronunciation? Standard American? Or Royal UK? Or Canadian, eh? So, even if I could speak using one proper variant, it still won’t be really native in many contexts.

    Vocabulary

    Obvious point with the same examples as before. But that’s English, the de-facto new Latin. Let me tell you an anecdote from Russian. I was born in the Northern Kazakhstan in the city on the border with Russia. You’d imagine not many vocabulary differences. Yet, the moment you cross the border everyone can tell that you’re from Kazakhstan just by one extremely common word: “sotka” (contraction for “a cell phone”). In Russia they use “mobilka” (contraction from “a mobile phone”). This reminds me of “smoke” vs “sanity” testing a lot.

    Words origins

    Some words with the same spelling can have completely distinct origins. For example, “bear”:

    • as for an animal came from Proto-Indo-European *bʰerH- (grey, brown) or *ǵʰwer- (wild animal)
    • as for “to sustain” came from Proto-Indo-European *bʰer- (to carry)

    And the opposite situation, when words with different spelling and meaning came from the same origin, e.g. “suit” and “suite”.

    People tend to make wrong assumptions about modern spelling and pronunciation. We think we understand words and their relative closeness. We don’t. Therefore, many everyday conclusions about them are also faulty. Those who try to be on a safe side refer to dictionaries, but here is a catch: dictionaries are opinionated language slices. That’s why there are so many dictionaries: general, jargon-specific, etymological. There is no single point of truth you can safely refer to. Isn’t it funny when you read news about some big-name dictionary finally including a word that was in use for a long time?

    By the way, in Russian “тестер” while being direct calque of “tester” is a name for devices like multimeter. Electrical engineers coined this term in Russian earlier than our role was invented (so we are named “тестировщик” ~ “testist”). It’s quite funny considering how much emphasize is there on testing being human activity.

    Evolution of languages

    All aforementioned are just smaller parts of overall language evolution. For some reason we are accustomed to perceive a language as something stagnant and with rigid unchanging rules. Maybe because it’s easier to teach like that in school? And nothing can be further from true because languages are perpetually in flux, either for historical or geographical reasons. Before it was happening naturally without many obstacles, but now we have schools, official authorities like Académie française, and beloved grammar-nazis.

    One of the best examples is an accentual system in Russian (stress). Whoever tried to learn how to pronounce Russian words would be certainly baffled how illogical it is. Natives make mistakes all the time. The reason is simple: current system is in the transition state from highly ordered and easy Proto-Slavic accent to the new someday also ordered but different accent. Yet right now transition is around 30% mark and it breaks havoc within speakers. What makes things worse are all those regulatory bodies and opinionated people who try to control this process and make you speak already obsolete way.


    I won’t be able to stop arguing about some words. Even those who say that they are sick and tired of some common testing debates, still nit-pick on other concepts. It’s a part of human nature which is common not only in testing community: recently I’ve read an article by Troy Hunt about which way of API versioning is better. And the most valuable lesson from there is:

    Unfortunately this very often gets philosophical and loses sight of what the real goal should be: building software that works

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/10/13/how-to-test-api-usability-part-1/index.html b/articles/2019/10/13/how-to-test-api-usability-part-1/index.html index df5e3b56..883db1d5 100644 --- a/articles/2019/10/13/how-to-test-api-usability-part-1/index.html +++ b/articles/2019/10/13/how-to-test-api-usability-part-1/index.html @@ -1,13 +1,10 @@ How to test API usability: part 1 | aviskase -

    How to test API usability: part 1

    +13 Oct 2019
    8 mins

    Disclaimer: this is a translation of the article written 2 years ago for a corporate blog. Bear in mind, at the moment of the writing I was testing SOAP services and Excel-based import/export at big government project, so most of the examples relate to that experience.


    Usability is one of the most crucial quality attributes of an API. Let’s talk about why, when, and how we can assess this characteristic.

    Today (hopefully) no one doubts the necessity of usability testing of GUI. Yet, according to ISO 9241, usability is the effectiveness, efficiency and satisfaction with which specified users achieve specified goals in particular environments. There is no mention of menus, fonts, or buttons color. Hence, we can evaluate usability of any product, be it a mobile app, a vacuum cleaner, or an API.

    For testing API usability we can use methods developed in the field of HCI; same as used for GUI. Generally, these methods can be divided into two categories: analytical and empirical.

    Analytical methods

    Analytical methods involve exploration based on some expert knowledge. Loosely speaking, you and/or the whole dev team try to evaluate and find hypothetical usability problems without users input.

    Heuristic evaluation

    Easiest way is to use heuristics. There are no strict lists of what criteria you should check; all depends on what kind of API you have (e.g., library or REST service).

    For instance, a paper on a structural analysis of usability problem categories mentions this set of heuristics:

    • Complexity. An API should not be too complex. Complexity and flexibility should be balanced. Use abstraction.
    • Naming. Names should be self-documenting and used consistently.
    • Caller’s perspective. Make the code readable, e.g. makeTV(Color) is better than makeTV(true).
    • Documentation. Provide documentation and examples.
    • Consistency and conventions. Design consistent APIs (order of parameters, call semantics) and obey conventions (get/set methods).
    • Conceptual correctness. Help programmers to use an API properly by using correct elements.
    • Method parameters and return type. Do not use many parameters. Return values should indicate a result of the method. Use exceptions when exceptional processing is demanded.
    • Parametrized constructor. Always provide default constructor and setters rather than a constructor with multiple parameters.
    • Factory pattern. Use factory pattern only when inevitable.
    • Data types. Choose correct data types. Do not force users to use casting. Avoid using strings if better type exists.
    • Concurrency. Anticipate concurrent access in mind.
    • Error handling and exceptions. Define class members as public only when necessary. Exceptions should be handled near from where it occurred. Error message should convey sufficient information.
    • Leftovers for client code. Make the user type as few code as possible.
    • Multiple ways to do one. Do not provide multiple ways to achieve one thing.
    • Long chain of references. Do not use long complex inheritance hierarchies.
    • Implementation vs. interface. Interface dependencies should be preferred as they are more flexible.

    Let’s try to apply some of these heuristics. -There was a time when every new tester came to me during the onboarding and asked about the error message “House with ID <> was not found.” I told them to use internal system id instead of global FIAS id (Russian index system for buildings). And every one looked startled and answered me that there is no such parameter in the API request! Well, the problem was that you had to use the same parameter named FIASHouseGUID. For some reason when system was designed no one thought that the better name would have been HouseID as it could be filled either with FIAS id or with internal id. Even though current name was misleading (naming heuristic), it was no longer possible to change without breaking backward compatibility.

    Next example is about error handling. One service I tested had a very common error “Access is denied.” There were numerous reasons for this error: no entitling documents, documents are not in the status “published,” other organization already created the same object. Reasons were different, but the received error message was the same; users couldn’t guess what was their problem.

    There are other, more “serious” heuristics for API. Often they target specific technical details. You need to be able to code to understand them. For example, criteria from Joshua Bloch. Or a usability research made by Microsoft to determine which constructor is better: default constructor with setters and getters or constructor with required parameters. Results showed that the first method was more preferable; this became a heuristic for library design.

    Cognitive dimensions

    These are distinct criteria used predominately for evaluating usability of notations, user interfaces, and programming languages — or, generally speaking, information artifacts. In my view, they intersect with some heuristics, but there is a difference: heuristics are contextually selected by some experts, whereas cognitive dimensions are more or less stable set of principles. You can read about the main set described by Thomas R.G. Green and Marian Petre in the Wikipedia.

    Some companies customize cognitive dimensions for their needs, like a framework suggested by Visual Studio usability group:

    • Abstraction level. The minimum and maximum levels of abstraction exposed by the API, and the minimum and maximum levels usable by a targeted developer.
    • Learning style. The learning requirements posed by the API and the learning styles available to a targeted developer.
    • Working framework. The size of the conceptual chunk (developer working set) needed to work effectively.
    • Work-step unit. How much of a programming task must/can be completed in a single step.
    • Progressive evaluation. To what extent partially completed code can be executed to obtain feedback on code behavior.
    • Premature commitment. The number of decisions that developers have to make when writing code for a given scenario and the consequences of those decisions.
    • Penetrability. How the API facilitates exploration, analysis, and understanding of its components, and how targeted developers go about retrieving what is needed.
    • API elaboration. The extent to which the API must be adapted to meet the needs of targeted developers.
    • API viscosity. The barriers to change inherent in the API and how much effort a targeted developer needs to expend to make a change.
    • Consistency. How much of the rest of an API can be inferred once part of it is learned.
    • Role expressiveness. How apparent the relationship is between each component exposed by an API and the program as a whole.
    • Domain correspondence. How clearly the API components map to the domain and any special tricks that the developer needs to be aware of to accomplish some functionality.

    Here is an example for domain correspondence. Service main entity was a house. Common apartment building can have several entryways, each leading to set of apartments. But in Kaliningrad this doesn’t apply: a typical address there can look like “2-4 Green Street,” where entryways are house 2 and house 4. This bizarre (and initially unknown) domain model broke the whole logic behind API design. For instance, we had to allow users to add house-level metering devices to entryways if it’s actually a house.

    Cognitive walkthrough

    While the first two methods are based on checking API against some kind of list of criteria, cognitive walkthrough is closer to scenario-based testing. Essentially, an expert comes up with typical API usage scenarios and attempts to perform them.

    Cognitive walkthrough example

    You can combine this method with heuristics. When we analyzed services, we found out that there were problems with the consistency: when you sent a request to create an entity, some services responded with entity version id, while others provided root id. Moreover, most of the services required entity id for creation of other entities, and again, it could be either root or version id. It didn’t look that bad, until we tried walking through a business scenario:

    1. Create an entitling document
    2. Create a metering device providing document root id

    In existing API workflow you had to do it in 3 steps instead of 2:

    1. Create an entitling document → server responds with document version id
    2. Retrieve the document using provided version id and get document root id from the response
    3. Create a metering device providing document root id

    This middle step is objectively unnecessary and generates additional server load. Here, using cognitive walkthrough, we also detected an inconsistency with heuristic “minimal working code size.”

    API peer review

    Heuristics and walkthroughs are great methods, but they could be quite subjective. For better objectivity you’d better use group evaluations, where several persons analyze API. You can read about how and why this method can find usability problems which are rarely found by empirical methods in this Microsoft paper.

    Peer reviews involve these four roles:

    • Usability expert who will organize and moderate the evaluation process from usability perspective
    • A person who is an owner for the specific API chunk under review
    • A person who is an owner of the API unit (or system) where the reviewed chunk resides and who knows the context of API usage and its interactions with other APIs
    • 3-4 persons who will complete some task which will be used to actually evaluate usability (usually just other developers)

    During the planning process, a usability expert and a chunk owner should discuss:

    • Key tasks to be completed during the review (e.g., how to create a document using an API)
    • Code examples to be reviewed
    • Who are the other participants (they can be selected based on specific criteria, like knowledge of SOAP services and Java)
    • Place and time for review session

    You should start a peer review session with the explanation of how this meeting will proceed and communicate basic information related to the evaluated API chunk. Next you distribute code examples and discuss them, asking these main questions:

    • Do you understand what this code does and what its objective is?
    • Is this objective achieved in logical and rational manner?

    Based on the answers, a usability expert asks to elaborate details. For example, hearing that naming is weird, expert should ask why a person thinks that way and what different name would be better.

    The final step is to analyze problems. Here is where an API unit owner can help to identify the most significant issues and could they be resolved or not.


    That’s the end of part one. Empirical methods are covered in part two.

    older +There was a time when every new tester came to me during the onboarding and asked about the error message “House with ID <> was not found.” I told them to use internal system id instead of global FIAS id (Russian index system for buildings). And every one looked startled and answered me that there is no such parameter in the API request! Well, the problem was that you had to use the same parameter named FIASHouseGUID. For some reason when system was designed no one thought that the better name would have been HouseID as it could be filled either with FIAS id or with internal id. Even though current name was misleading (naming heuristic), it was no longer possible to change without breaking backward compatibility.

    Next example is about error handling. One service I tested had a very common error “Access is denied.” There were numerous reasons for this error: no entitling documents, documents are not in the status “published,” other organization already created the same object. Reasons were different, but the received error message was the same; users couldn’t guess what was their problem.

    There are other, more “serious” heuristics for API. Often they target specific technical details. You need to be able to code to understand them. For example, criteria from Joshua Bloch. Or a usability research made by Microsoft to determine which constructor is better: default constructor with setters and getters or constructor with required parameters. Results showed that the first method was more preferable; this became a heuristic for library design.

    Cognitive dimensions

    These are distinct criteria used predominately for evaluating usability of notations, user interfaces, and programming languages — or, generally speaking, information artifacts. In my view, they intersect with some heuristics, but there is a difference: heuristics are contextually selected by some experts, whereas cognitive dimensions are more or less stable set of principles. You can read about the main set described by Thomas R.G. Green and Marian Petre in the Wikipedia.

    Some companies customize cognitive dimensions for their needs, like a framework suggested by Visual Studio usability group:

    • Abstraction level. The minimum and maximum levels of abstraction exposed by the API, and the minimum and maximum levels usable by a targeted developer.
    • Learning style. The learning requirements posed by the API and the learning styles available to a targeted developer.
    • Working framework. The size of the conceptual chunk (developer working set) needed to work effectively.
    • Work-step unit. How much of a programming task must/can be completed in a single step.
    • Progressive evaluation. To what extent partially completed code can be executed to obtain feedback on code behavior.
    • Premature commitment. The number of decisions that developers have to make when writing code for a given scenario and the consequences of those decisions.
    • Penetrability. How the API facilitates exploration, analysis, and understanding of its components, and how targeted developers go about retrieving what is needed.
    • API elaboration. The extent to which the API must be adapted to meet the needs of targeted developers.
    • API viscosity. The barriers to change inherent in the API and how much effort a targeted developer needs to expend to make a change.
    • Consistency. How much of the rest of an API can be inferred once part of it is learned.
    • Role expressiveness. How apparent the relationship is between each component exposed by an API and the program as a whole.
    • Domain correspondence. How clearly the API components map to the domain and any special tricks that the developer needs to be aware of to accomplish some functionality.

    Here is an example for domain correspondence. Service main entity was a house. Common apartment building can have several entryways, each leading to set of apartments. But in Kaliningrad this doesn’t apply: a typical address there can look like “2-4 Green Street,” where entryways are house 2 and house 4. This bizarre (and initially unknown) domain model broke the whole logic behind API design. For instance, we had to allow users to add house-level metering devices to entryways if it’s actually a house.

    Cognitive walkthrough

    While the first two methods are based on checking API against some kind of list of criteria, cognitive walkthrough is closer to scenario-based testing. Essentially, an expert comes up with typical API usage scenarios and attempts to perform them.

    Cognitive walkthrough example

    You can combine this method with heuristics. When we analyzed services, we found out that there were problems with the consistency: when you sent a request to create an entity, some services responded with entity version id, while others provided root id. Moreover, most of the services required entity id for creation of other entities, and again, it could be either root or version id. It didn’t look that bad, until we tried walking through a business scenario:

    1. Create an entitling document
    2. Create a metering device providing document root id

    In existing API workflow you had to do it in 3 steps instead of 2:

    1. Create an entitling document → server responds with document version id
    2. Retrieve the document using provided version id and get document root id from the response
    3. Create a metering device providing document root id

    This middle step is objectively unnecessary and generates additional server load. Here, using cognitive walkthrough, we also detected an inconsistency with heuristic “minimal working code size.”

    API peer review

    Heuristics and walkthroughs are great methods, but they could be quite subjective. For better objectivity you’d better use group evaluations, where several persons analyze API. You can read about how and why this method can find usability problems which are rarely found by empirical methods in this Microsoft paper.

    Peer reviews involve these four roles:

    • Usability expert who will organize and moderate the evaluation process from usability perspective
    • A person who is an owner for the specific API chunk under review
    • A person who is an owner of the API unit (or system) where the reviewed chunk resides and who knows the context of API usage and its interactions with other APIs
    • 3-4 persons who will complete some task which will be used to actually evaluate usability (usually just other developers)

    During the planning process, a usability expert and a chunk owner should discuss:

    • Key tasks to be completed during the review (e.g., how to create a document using an API)
    • Code examples to be reviewed
    • Who are the other participants (they can be selected based on specific criteria, like knowledge of SOAP services and Java)
    • Place and time for review session

    You should start a peer review session with the explanation of how this meeting will proceed and communicate basic information related to the evaluated API chunk. Next you distribute code examples and discuss them, asking these main questions:

    • Do you understand what this code does and what its objective is?
    • Is this objective achieved in logical and rational manner?

    Based on the answers, a usability expert asks to elaborate details. For example, hearing that naming is weird, expert should ask why a person thinks that way and what different name would be better.

    The final step is to analyze problems. Here is where an API unit owner can help to identify the most significant issues and could they be resolved or not.


    That’s the end of part one. Empirical methods are covered in part two.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/10/13/how-to-test-api-usability-part-1/walkthrough_hucf44db6a2892b2831f76d1825a1f5316_40674_442x200_resize_q75_h2_box_3.webp b/articles/2019/10/13/how-to-test-api-usability-part-1/walkthrough_hu1563830899712024874.webp similarity index 100% rename from articles/2019/10/13/how-to-test-api-usability-part-1/walkthrough_hucf44db6a2892b2831f76d1825a1f5316_40674_442x200_resize_q75_h2_box_3.webp rename to articles/2019/10/13/how-to-test-api-usability-part-1/walkthrough_hu1563830899712024874.webp diff --git a/articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_barchart_huc3592e0d261c0d603c1362608bc29a04_10983_441x232_resize_q75_h2_box_3.webp b/articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_barchart_hu2391116665159472627.webp similarity index 100% rename from articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_barchart_huc3592e0d261c0d603c1362608bc29a04_10983_441x232_resize_q75_h2_box_3.webp rename to articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_barchart_hu2391116665159472627.webp diff --git a/articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_chart_hu461e5d787a4fa64fc65e79b4a35177ba_35949_286x286_resize_q75_h2_box_3.webp b/articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_chart_hu3091947346510474851.webp similarity index 100% rename from articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_chart_hu461e5d787a4fa64fc65e79b4a35177ba_35949_286x286_resize_q75_h2_box_3.webp rename to articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_chart_hu3091947346510474851.webp diff --git a/articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_stats_hu99fb22af3f108385a6c53063d53e3513_23102_457x207_resize_q75_h2_box_3.webp b/articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_stats_hu1855512388614765158.webp similarity index 100% rename from articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_stats_hu99fb22af3f108385a6c53063d53e3513_23102_457x207_resize_q75_h2_box_3.webp rename to articles/2019/10/19/how-to-test-api-usability-part-2/api_ux_stats_hu1855512388614765158.webp diff --git a/articles/2019/10/19/how-to-test-api-usability-part-2/index.html b/articles/2019/10/19/how-to-test-api-usability-part-2/index.html index 20d8f9a3..5af407ee 100644 --- a/articles/2019/10/19/how-to-test-api-usability-part-2/index.html +++ b/articles/2019/10/19/how-to-test-api-usability-part-2/index.html @@ -1,15 +1,27 @@ How to test API usability: part 2 | aviskase

    How to test API usability: part 2

    +19 Oct 2019
    +6 mins

    This is part two of a two-parter. Check out part one.

    Empirical methods

    The distinction between analytical and empirical methods is that the latter investigates how real users will use the product.

    But don’t assume that empirical methods are by default better than analytical: both are important because they discover different problems. This research showed that heuristics were more efficient in finding documentation and structural problems, whereas empirical methods were more useful in finding UX and runtime specific issues.

    Barchart with comparison of different issue types found via different methods

    Monitoring

    Monitoring is used to gather usage statistics. For web services, it’s rather easy. For instance, you can discover that one API endpoint is never called. Hence, you should consider the causes: is it missing in the documentation or not needed to anyone? Monitoring also helps to map scenarios: what kind of requests, to which services, and in what order happen most often.

    And don’t forget to monitor not only successful requests but also failures. Imagine, some business errors are stockpiling: maybe you need to reconsider API design or error handling?

    Another thing to monitor is data volumes. Analysts from the project I worked on assumed that type A documents should be more common than type B documents, so the service was better optimized for the first type. It was quite a surprise when we did a simple SQL count and found out that the number of type A documents were 600 thousand, while type B accounted for 80 million. After that discovery, we had to prioritize tasks related to service B way higher.

    Support tickets

    If you have a support team, you’re in luck: analyze tickets, pick out those related to usability, and identify the most serious issues. Previously I wrote about accidental Cyrillic symbols instead of English in service schema: these problems resurfaced specifically via support.

    Moreover, support tickets offer insight into the most common tools and workflows used to work with your API. Once we had an external developer who generated soapAction dynamically based on a root request structure by trimming the word Request. For example, importHouseRequest gave importHouse. But one of our services with a name importPaymentDocumentRequest expected soapAction=importPaymentDocumentData instead of importPaymentDocument (what the developer would expect). On the one hand, the user’s solution was poor: you’d better use WSDL. On the other hand, maybe they didn’t have a choice and we probably should ask ourselves why naming wasn’t consistent.

    Surveys

    Not everyone has a support service. Or perhaps it doesn’t give enough information. In that case, surveying API users is helpful. There is no point in giving examples: this topic is highly contextual. But you can start with the basics: “What do you like?”, “What do you don’t like?”, and “What would you like to change?”.

    User sessions

    User sessions are the most expensive and cumbersome usability evaluation method. You need to find people based on a typical user profile, give them some tasks, watch the process, and analyze results.

    Each company administers sessions in its own way. Some perform remote sessions, others invite developers on site. In both cases developers can use their own laptops and favorite IDEs: first, it’s closer to real-world conditions, second, it minimizes stress from an unknown environment.

    Yet, there are more exotic examples. A developer is led to the room with a one-way mirror (yup, like in movies). A usability expert sits behind the mirror and observes developer actions as well as what’s happening on the dev’s computer screen. The developer can ask questions, but the expert will answer only in rare cases. In my opinion, it’s too sterile.

    Generally, all API related user sessions have two phases. The first phase is a developer workshop with tasks like:

    • Solve a problem in the notebook without an IDE (to get an idea of how developers would design API on their own).
    • Practical tasks for API usage (e.g., write a code for file upload using a service).
    • Read and review a code snippet to assess its clarity and readability (use printouts to make this task more challenging).
    • Debug a faulty code snippet (this helps to study how a user will handle and correct an error).

    The second phase is an interview where you ask:

    • Name three biggest issues you encountered during the workshop and how did you overcome them (documentation, support, StackOverflow, a friend’s help)?
    • How much time did you spend looking for additional information outside official documentation?
    • Did you encounter unexpected error messages? If yes, did they help you correct a problem?
    • Name at least three ways to improve official documentation.
    • Name at least three ways to improve API design.

    Personas

    Personas are used both in analytical and empirical methods. All you need is to figure out which characteristics best describe your users (in case of API, developers). These descriptions tend to be humanized by assigning a name and a photo, adding information about fears and preferences. You can wear a “persona hat” while applying heuristics or rely on them while selecting developers for user sessions.

    Typical developers’ personas:

    • Systematic developers don’t trust API and write code defensively. They are usually deductive, write on C++, C, or even Assembly.
    • Pragmatic developers are more common and work both in deductive and inductive manners. Typically they code desktop and mobile apps in Java or C#.
    • Opportunistic developers concentrate on solving business problems in an exploratory and inductive fashion. Guess what language they prefer? JavaScript.

    Now, I want to point out that the aforementioned language discrimination is not my invention. If you’re lucky, perhaps you’ll find the original article by Visual Studio usability expert, where these quirky definitions were introduced. Unfortunately, I was able to get only [its first page in the Wayback Machine][visual-studio], so you have to take my word for it. Nevertheless, I hope this example can encourage you to create your own personas.

    We can also combine personas with cognitive dimensions. Create a radar chart with 12 axes, where each axis is a cognitive dimension. Next, plot current values for your API and values according to the persona’s expectations. This chart is great for comparing how existing API corresponds to user values.

    Radar chart with comparison of developer expectations vs current state of API

    Developer from the example chart (blue line) prefers API with a high level of consistency (10) and hates writing boilerplate (4). As we can see, the current state of API (black line) doesn’t satisfy these criteria.

    Summing up

    Readers comfortable with GUI usability testing would say: “That’s exactly the same stuff!”. And you’re right, there is nothing supernatural about API usability. Even though it’s called an application programming interface, programs are yet to learn how to find other APIs and use them automatically; they still need us, meatbags. That’s why almost everything applied for GUI usability evaluation is reusable for API with some adjustments.

    Now, what about the best method? None, apply them all! According to this research, each method can identify unique issues.

    Venn diagram showing how different methods overlap in finding different issues

    If you are tight on resources, I suggest using the least expensive methods: heuristics, cognitive dimensions, walkthrough, and support tickets. Even the simplest techniques can drive API improvements.

    Someone would argue that API usability is not that important: “we don’t have time for that, it’s a dev thingy.” But developers created style guides not just to be fancy; this serves to accelerate the achievement of shippable quality. We care about hidden code quality, therefore we need to care about externally visible code like APIs even more.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/10/26/a-testers-guide-on-hunting-for-api-related-sources/index.html b/articles/2019/10/26/a-testers-guide-on-hunting-for-api-related-sources/index.html index ea254683..95a39491 100644 --- a/articles/2019/10/26/a-testers-guide-on-hunting-for-api-related-sources/index.html +++ b/articles/2019/10/26/a-testers-guide-on-hunting-for-api-related-sources/index.html @@ -2,12 +2,11 @@

    A tester’s guide on hunting for API related sources

    +26 Oct 2019
    +4 mins

    You’ve got interested in APIs. Or you’re not a fan (yet) but you have to test it. Whatever the cause, you’d want to develop a mental model of this vast field. And a model construction demands a generous supply of information to consume and digest.

    I prefer to seek knowledge in these five areas:

    • standards
    • dev experience
    • usability
    • tech writers
    • vendors and companies

    In this article, I’ll explain a bit why each area matters and give a bunch of sources. I won’t go into details why I selected these sources in particular: some just came to my mind first. Treat them as examples, and if you know not mentioned must-reads, I’m happy to learn about them.

    Standards

    Standards are the foundation. And reading abridged explanations in Wikipedia or someone’s blog is never enough. True understanding requires the ability to read and reason based on original documents. Details do matter for APIs.

    For web services, the most valuable sources are RFC, API description and schema specifications. While some of them are community-driven, others, like GraphQL or gRPC, are backed by companies.

    Dev experience

    Barebone foundation is important, but let’s add some meat. While you can try to speculate about practical differences between GraphQL and REST+HTTP/2, you’ll learn faster from those who develop and use APIs as their career. I’m talking about developers, of course. For some inexplicable reason, some so-called “professionals” still perpetuate a myth about testers not being able to understand devs’ books and articles, so prove those haters wrong!

    Usability

    I like API usability very much. Compared to performance or security, this theme is often overlooked. HCI is the whole field of study with real research and statistics magic, which may feel overwhelming at first. Here is a selection of papers to begin with.

    By the way, there is a term in HCI: developer’s experience. DX is like UX, but when a user is a developer.

    Tech writers

    I insist that without proper documentation an otherwise perfect API is still a shit. And who knows about docs better than technical writers? APIs are a hot topic for them, simply because that’s higher-paying field.

    On the side note, it’s curious from the tester’s viewpoint that tech writers have the same holy war about how “technical” they should be. Cute, isn’t it?

    Vendors and companies

    Almost all IT companies now have blogs and even conferences, and those prove to be an excellent source. I’d suggest paying attention to:

    Though, be skeptical. The former tries to sell their tools, whereas the latter tend to show-off.

    Testers

    Some of you are probably asking where are recommendations on testers? Well… fuck testers.

    Don’t even bother reading attentively testers’ blogs about API. Don’t make my mistake! I’ve lost an unimaginable amount of time doing that: oftentimes, all of them can be divided into three categories:

    1. How to apply well-known testing methods and techniques to APIs.
    2. Basic theory on how APIs work.
    3. Tutorials on using <insert a library or a tool name> to test API. All those RestAssured, Karate, you name it.

    Don’t get me wrong, I do understand that I’m also an offender and write similar articles. There is some value in them. For me, it’s a way to sort my thoughts. And reading still helps making sense on what to look for. Moreover, if you’ve just started your tester’s journey, perhaps you’re not so comfortable with test theory, then stories about its application are useful. Nevertheless, I can go on a day-long rant about how learning only from testers stupefies you.

    So, heed my advice. I bet you already follow all those testers and see API related stuff from them once in a while. Skim through, there could be some interesting info, but don’t rely on them. Fun fact: the most popular talks on the Heisenbug conference are from non-testers. Testing can never be an idea by itself; you need a practical application to other fields to give testing a sense and direction. That’s why studying those fields proves to be productive and enlightening. Always hunt for other sources!


    See also:

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/11/11/weekly-hmms-__init__/index.html b/articles/2019/11/11/weekly-hmms-__init__/index.html index cdf3c510..505cebc6 100644 --- a/articles/2019/11/11/weekly-hmms-__init__/index.html +++ b/articles/2019/11/11/weekly-hmms-__init__/index.html @@ -1,10 +1,21 @@ Weekly hmms: __init__ | aviskase -

    Weekly hmms: __init__

    +11 Nov 2019
    +3 mins

    __init__

    This post initializes a series of weekly ponderings, interesting links, and other hmms. Think of it as typical a “Five for Friday,” but without number constraint and more emphasis on effects on my thought process. These posts should come out on Fridays, but because I forgot to commit, this one is late.

    Oh, and if you have no idea what __init__ is, that’s from Python.

    “Write the Docs” podcast: episode 25

    I’ve just started plunging in and out tech writers’ media, and so far it keeps amusing me. WTD #25 is interesting for two things.

    First, this episode covers research on how developers use API documentation. Unlike predominant web services based papers, this research focuses on C++. One discovery was that developers prefer checking header files (aka interface definitions) to implementation or documentation.

    Second, I noticed again that we, testers, and API tech writers have a common identity crisis: given enough time, developers can do our work. One guy from the podcast sounded relieved when he heard that not all the docs and comments in Google are written by devs. Maybe Alan and Brent should pitch modern testing principles to tech writers too?

    Read the damn code

    This week testing slack group had an almost holy war about testers looking at code. The consensus seemed to be “access to code is awesome,” nevertheless, there were other opinions:

    You should have a strong bias seeing the code. It will be more difficult to search for the unknowns.

    Oh my. I don’t do it as often as I should, but I love checking commits:

    • Skimming through commit messages for the current build.
    • Checking what and how was touched for implemented tasks.
    • Reading commits associated with bug fixes.

    And every time I uncover some unknowns. Just very recent examples:

    • Caught that the task for implementing API blah-blah also has commits for API meh-meh. Not only I wouldn’t know about these changes without the Git God, but also those were scheduled with a different design for later.
    • Identified code duplication and asked the dev to fix the bug in the remaining dups or refactor the code (hehe, sorry, Eric).
    • Suspected that the bug fix was incomplete, asked dev, he confirmed and refixed. Not spending time on build&install&test is priceless.

    So, dear testers, read the damn code. Stop behaving like special snowflakes whose mind will be forever damaged if you’ll learn to code a bit. I learned Pascal at school. My father taught himself on paper using journal articles. That’s not rocket science. The way modern society goes, coding is close to become a part of common literacy. No one asks for enterprise-levels skill, but as long as your system isn’t written in Brainfuck, even basics should be fine.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/11/16/weekly-hmms-valuations-job-titles-conferences-apis/index.html b/articles/2019/11/16/weekly-hmms-valuations-job-titles-conferences-apis/index.html index b5604151..e484bccf 100644 --- a/articles/2019/11/16/weekly-hmms-valuations-job-titles-conferences-apis/index.html +++ b/articles/2019/11/16/weekly-hmms-valuations-job-titles-conferences-apis/index.html @@ -1,16 +1,27 @@ Weekly hmms: valuations, job titles, conferences, APIs | aviskase

    Weekly hmms: valuations, job titles, conferences, APIs

    +16 Nov 2019
    +3 mins

    To assume I could push anything on Fridays was too optimistic. Thus, I’ll commit to Saturdays.

    Let’s start.

    SaaS valuations

    My current company, NetGovern, operates with an open-book management mindset. Apart from other information, all employees know company finances so we can better understand what is our real position and where we plan to go next.

    It’s tough to wrap your head around balance sheets or cash flows, especially for me: I was born in 1992, a year after Kazakhstan declared independence from the Soviet Union. Suddenly, generations raised in the planned economy plunged into the free market (didn’t go well). I’ came from the culture of mixed and uncertain economic literacy, hence, all these stocks, investing, growth rates and margins don’t flow naturally into my brain.

    Nevertheless, I don’t shy away from these topics because learning anything is the best hobby. This week, we read a paper explaining how to valuate typical SaaS companies (how to calculate a price).

    I’ve created a mind map (or an outline, Mindomo supports both at the same time) with the main info:

    Some tech writers avoid the word “writer” in their titles

    The last myth from the podcast “10 myths about API documentation” is: people will respect you more if the word “writer” isn’t in your job title.

    Insecure tech writers prefer to call themselves:

    • Information developers
    • User assistance developers
    • Information strategists
    • Content strategists
    • Technical communications professionals
    • Content engineers

    First, these are so funny. Second, the testing field has the same tendency. My official job title is “QA & escalations analyst,” whatever this means. Testers don’t avoid the word “test,” but they do try to shoehorn hardcoreness with words “developer,” “engineer,” or “analyst.”

    OnlineTestConf

    It’s not a secret that I’m not a fan of testing-only conferences. Not rolling your eyes is impossible when you see mind-blowing prices for the STAR conference and find there popular workshops like “Learning Git.” Pass.

    Still, I checkout out some occasionally. Heisenbug is amazing. But most of the talks are in Russian, sorry folks.

    The next available conference is OnlineTestConf, December 3rd-4th 2019. The previous one was passable, this one seems to have a similar structure. Still, nothing beats free and online. There will be big names, yet what caught my eye was the “Adidas Testing Platform” talk. Adidas API guidelines are awesome, and learning more about their processes would be cool.

    Me and APIs

    Should this be a regular rubric? Perhaps.

    Though I love APIs, my knowledge is unsatisfactory and insufficient to be confident in my new undertaking: writing API design guidelines. Without this document, your APIs will soon become an inconsistent mess. You need to have conventions on even the most basic stuff like capitalization or pluralization. We should have created this doc a year ago; doing consolidation and standardization will require painful fixing of existing clients, but better sooner than later.

    As I said, I’m not an expert on API design. And I’m not a good writer either. I’m a tester whose job description is to find ways to improve quality. Next months will be fun: doing research, poking devs with sticks, and asking stupid questions in the “APIs You Won’t Hate” slack group.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/11/23/weekly-hmms-fowler-collaborations-html-and-cts/index.html b/articles/2019/11/23/weekly-hmms-fowler-collaborations-html-and-cts/index.html index c8dc7daf..07e51ab8 100644 --- a/articles/2019/11/23/weekly-hmms-fowler-collaborations-html-and-cts/index.html +++ b/articles/2019/11/23/weekly-hmms-fowler-collaborations-html-and-cts/index.html @@ -1,13 +1,24 @@ Weekly hmms: Fowler, collaborations, HTML, and CTS | aviskase

    Weekly hmms: Fowler, collaborations, HTML, and CTS

    +23 Nov 2019
    +3 mins

    New week, new hmms!

    Martin Fowler and exploratory testing

    All testers’ slack groups, forums, and blogs erupted this week with the tidings of joy: Martin Fowler wrote a post about exploratory testing. Of course, it seems a bit late and cursory, but at least now we have a very respected source to point to.

    Collaborations

    Mob programming

    Finally found time to read a “Mob Programming Guidebook” by Maaret Pyhäjärvi. I’m not entirely sure about applying it at the moment, but it looks like an interesting method for knowledge sharing facilitation.

    And contributions style roles can be reused as hats in other activities:

    • driver (intelligent input device)
    • sponsor (supporting others from a unique position)
    • nose (noticing things about the code)
    • navigator (translating ideas into code)
    • researcher (having better information available)
    • automationist (recognizing repetition)
    • conductor (enhancing others contributions)
    • mobber (always contributing in different ways)
    • rear admiral (helping designated navigator do better and learn)
    • archivist (improving team visibility)

    Tips for code review

    Google Testing blog is still alive and has posted a nice short guide about being a good code reviewer. The best thing is that there are not only tips for reviewers but also for authors. Likewise, they could and should be applied for issue reports and other communications.

    Validation for email inputs

    Standards are weird. We had a tiny funny problem this week with an OpenAPI spec and a format: email. The context:

    • SwaggerUI generates input fields, and with this format it will have type=email.
    • When I don’t care about cross-browser stuff, I use Chrome.
    • Our middleware seems to validate email formats, somewhere very deep in dependencies.
    • We also have a custom validation.

    First, I checked a request with a garbage email. It failed on the middleware layer. Then I checked an email with non-ASCII characters, but our validation failed. That meant that validation in middleware was passing; therefore, its validation checks were better than ours.

    The dev who worked on fixing the bug noticed an interesting behavior. If you use Firefox to open SwaggerUI, it will add a red highlighting for an email input field when a value contains non-ASCII characters. According to the MDN docs, this is a known problem due to HTML5 issue. The specification proposes using a simple regex for validation!

    Chrome does not complain about internationalized values. And it means that Firefox implemented HTML5 spec too well, introducing a confusing behavior. Oops.

    Carpal tunnel syndrome

    I wasn’t diagnosed with CTS (yet), but my posture at home is awful, so my right hand hurts. I don’t have a proper desk and usually I sit with a laptop at a round table. Thus, there is not enough room to position a “mouse” hand.

    A year ago I bought a cheap Anker vertical mouse. It’s great, even with my shitty habits I have had no pain. But it’s wired, and soon I’ll need a wireless one. Logitech is always my first choice in this case because it doesn’t drain batteries too fast. Unfortunately, their only vertical mouse is unreasonably expensive, so I’ve decided to experiment with their M570 trackball.

    It’s cool that you don’t need to move your hand at all, but the pain has returned: it looks like vertical mouse works better for small spaces. For a trackball, you should be able to lay down your hand somewhere fully. Though, the more expensive trackball model can be vertically adjusted. Anyway, I’ll get a real own desk soon, will see how it goes.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/11/25/why-i-dont-use-postman/index.html b/articles/2019/11/25/why-i-dont-use-postman/index.html index 5c22feed..f60e8a7d 100644 --- a/articles/2019/11/25/why-i-dont-use-postman/index.html +++ b/articles/2019/11/25/why-i-dont-use-postman/index.html @@ -1,13 +1,12 @@ Why I don’t use Postman | aviskase

    Why I don’t use Postman

    +25 Nov 2019
    +3 mins

    Being an API person, many people would expect me to use Postman. It’s the most well-known tool for HTTP-based APIs and it’s so ubiquitous that some use it even for SOAP (not the best idea ever).

    I did use Postman. My gist with installation scripts for Linux was so popular that Postman support team reached me out when it started causing non-obvious issues with updates. I also used this tool for internship courses. Yet I won’t do it anymore and let me explain why.

    I’m not a tech writer

    Postman isn’t just a tool for testing: it’s often used for writing documentation. I’m a tester, thus, this use case does not apply to my day-to-day work. Maybe it can be useful for your organization, though I don’t approve of the inability to self-host.

    I’m confused by GUI-driven tools

    I still have PTSD from SoapUI. It’s the best exploratory testing tool for SOAP services, but, damn, scenarios more difficult than request+response are brain busters. Many people don’t have problems with GUI-driven stuff:

    • JMeter for load testing
    • UI git apps
    • Point&click tools for web automation

    For me, all these are torture devices for anything more involved than two clicks. It’s something psychological, because if GUI tool requires tinkering with LEGO-like loop/if steps or pre/post-scripts, my nerve cells start audibly screeching. I’m not a command line nerd and I use these tools for certain activities, but not for complex automation.

    I can code

    Here we go to the obvious part: I am comfortable with code. Yeah, shitty smelling code, nevertheless, I find it way more intuitive to write a for loop purely in a programming language than to glue together pieces of JavaScript with GUI-level bits.

    My language of choice is Python because it’s very easy to scribble down a working script. It also has packages for any API needs imaginable, be it Requests, Bravado, Zeep, or Yandex.Tank.

    I use Insomnia

    Until recently I still used Postman a bit. I switched to Insomnia for mostly emotional reasons:

    • Insomnia is open-source.
    • Postman is bloated with features I don’t need.
    • There is too much Postman around. They even organize a conference now! I wonder is there a certification somewhere already.

    Insomnia is a case where less is more. I hope its recent acquisition by a bigger company won’t be detrimental, but being open-source we can always fork it.

    Though it’s not the only tool I use for exploratory API testing. My general patterns are:

    • Rapid data creation or testing simple requests: curl
    • Requests with bigger payloads: SwaggerUI or Insomnia
    • Chained, looped, or other complex stuff: reusing bravado-based adapters from automation framework

    And for actual automation, it’s code and code only.


    Your brain is your brain. Your context is your context. Mine resulted in avoiding GUI-driven tools. Research, try different approaches, and don’t simply default to the most popular choice.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/11/30/weekly-hmms-watching-reading-learning/index.html b/articles/2019/11/30/weekly-hmms-watching-reading-learning/index.html index eb7fd7b3..edae2621 100644 --- a/articles/2019/11/30/weekly-hmms-watching-reading-learning/index.html +++ b/articles/2019/11/30/weekly-hmms-watching-reading-learning/index.html @@ -1,20 +1,37 @@ Weekly hmms: watching, reading, learning | aviskase -

    Weekly hmms: watching, reading, learning

    +30 Nov 2019
    2 mins

    Watching

    During lunch, I watch videos from conferences. This week my favorites were:

    The second one has a nice quotable passage:

    A good API will let you be lazy.

    A great API will empower you to be really lazy.

    Reading

    Started reading:

    • The Design of Web APIs by Arnaud Lauret. I’ve got a hard copy, yay!

    • Universal Principles of Design by William Lidwell, Kritina Holden, and Jill Butler. There is a newer shorter edition, but I decided to try out the original one first. It was available in the local library, but I had to wait almost two months in the request queue: the book is old yet still relevant.

    Learning

    Alan and Brent talk a lot about basing feature development on testable hypotheses. Brent mentioned that he doesn’t even use the word “requirements” anymore.

    And just in time came a Coursera’s newsletter with the link to -Hypothesis-Driven Development course, which is a part four out of five in the agile development specialization. So, ideally, you shouldn’t start with it. Nevertheless, I’ve finished the first week’s materials and looks like time spent will be worthwhile.

    Though, there are some problems:

    • The instructor seems to sell his “venture design process.” That doesn’t make that methodology inherently bad, but keep it in mind.
    • The way he talks makes hard for me to concentrate. Too choppy.

    The course is short, so I’ll give it a chance (perhaps to the whole specialization).

    older +Hypothesis-Driven Development course, which is a part four out of five in the agile development specialization. So, ideally, you shouldn’t start with it. Nevertheless, I’ve finished the first week’s materials and looks like time spent will be worthwhile.

    Though, there are some problems:

    • The instructor seems to sell his “venture design process.” That doesn’t make that methodology inherently bad, but keep it in mind.
    • The way he talks makes hard for me to concentrate. Too choppy.

    The course is short, so I’ll give it a chance (perhaps to the whole specialization).

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/12/07/weekly-hmms-communities-laptops-linux/dell_keyboard_hud55af38982e5546e9dc1c41a4737eebd_171642_775x500_resize_q75_h2_box.webp b/articles/2019/12/07/weekly-hmms-communities-laptops-linux/dell_keyboard_hu10910225530400659624.webp similarity index 100% rename from articles/2019/12/07/weekly-hmms-communities-laptops-linux/dell_keyboard_hud55af38982e5546e9dc1c41a4737eebd_171642_775x500_resize_q75_h2_box.webp rename to articles/2019/12/07/weekly-hmms-communities-laptops-linux/dell_keyboard_hu10910225530400659624.webp diff --git a/articles/2019/12/07/weekly-hmms-communities-laptops-linux/index.html b/articles/2019/12/07/weekly-hmms-communities-laptops-linux/index.html index 2c275fdd..057d65fb 100644 --- a/articles/2019/12/07/weekly-hmms-communities-laptops-linux/index.html +++ b/articles/2019/12/07/weekly-hmms-communities-laptops-linux/index.html @@ -1,13 +1,24 @@ Weekly hmms: communities, laptops & Linux | aviskase

    Weekly hmms: communities, laptops & Linux

    +7 Dec 2019
    +4 mins

    This time, the proper title would be “Weekly Arghs.”

    Testing communities

    When I decided to become a tester, the first-ever topic I encountered was discussions rants about “ISO/IEC/IEEE 29119 Software Testing” set of standards. I was fresh out of the university, so I couldn’t have legal access to these documents, and naturally, I sided with opponents. Good or bad (mostly bad), I don’t care: if you wish to have guidelines or standards for the profession, it must be freely accessible to everyone. The end.

    But that’s not the thing I want to talk about. It’s just an example of my first deep dive into testing: it was a sign of what to come. Basically, everything is dividing our community: terminology, schools of thoughts, views. Dammit, testing in itself is a tiny thingy, we mostly reuse and apply knowledge and methods from other fields. Nevertheless, we keep constantly bickering with each other.

    I’m not a saint either. I rant, joke, correct, discuss, persuade, and roll my eyes. Maybe it comes with the profession. Or it’s a normal thing for any community. What amazes me, that this much concentration of disagreements exists around the pettiest and smallish topics. Sometimes people even talk about the same ideas but with different examples, and it results in confrontations (direct or indirect).

    Giving recent cases wouldn’t be humanly correct in regard to individuals involved. Let’s just say: I participate in several slack workspaces. And it’s quite common to a discussion in one to provoke posts in others with opposite reactions like “good work” and “it drives me insane.”

    Though, I want to quote one:

    Testing vs Checking is like Russian revolution — senseless and merciless.

    You can replace “testing vs checking” with whatever you want, and it will hold true to a lot of prevalent discussions.

    Laptops & Linux

    I’ve got a new laptop: Dell XPS15 7590. My old one is a Lenovo G560, almost 9 years old. It served me well, but it’s got too hard to keep even just Pycharm and Firefox running side by side. I squeezed every last bit out if it: maxing out memory to 8GB, adding SSD, using Fedora with a lighter desktop environment (XFCE). Still, its time has come.

    XPS is a good choice for several reasons:

    • extensible: I’ll be able to add more memory (32GB), change SSD or WiFi card, add HDD (but with a smaller battery)
    • Linux friendly
    • good battery
    • there was an awesome discount which is significant because I didn’t want to go wild with work budget

    But there are some disappointments. Let’s start with the objective one: keyboard.

    This is my old keyboard: it has Numpad, all keys, and even separate volume controls.

    Lenovo G560 keyboard

    This is the new one:

    Dell XPS15 keyboard

    I begrudgingly accepted not to have a Numpad and volume control keys, yet I didn’t even consider that there wouldn’t be separate Home, End, PgUp, and PgDn! Also, no right Fn key. Wow. What bewilders me more is that a previous model did have separate keys: but people complained about it on the Dell forum. You, guys, I hate you. You’re exactly like from that XKCD comic strip.

    Next disappointment is a display. Beware, this will be unexpected… I don’t like HiDPI. I’m so accustomed to shitty displays, that better ones feel wrong and uncomfortable. Colors and images are fine, but text, yikes. Letters are not crisp enough, there is something smudgy about them.

    I can already hear that it’s because I’m on Linux. NOPE. This laptop came with Windows 10 (I left it in a dual boot), but the text is blurry too. Even more, I think Linux actually does better jobs with antialiasing, because old Windows apps look particularly horrible.

    Maybe someone will say that I should try 4k or Mac with Retina. Thanks, but no. I saw those displays too. First, I don’t understand, why people keep using them without any scaling: font size is unbearably small. Second, I still don’t like it.

    So yeah, completely subjective feelings with physical problems: my eyes are way more strained now. Habits die hard.

    Oh, and I also switched from XFCE to Gnome, and this drives me crazy even more. My first Linux was Ubuntu with Gnome 2, and, surprisingly, UX in XFCE is closer to it than in Gnome 3. I’m too lazy to reinstall again, so, I guess, I’ll try to relearn.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/12/07/weekly-hmms-communities-laptops-linux/lenovo_keyboard_hu7103f81b0bac2c90aa84d23e3ec1b381_72090_752x461_resize_q75_h2_box.webp b/articles/2019/12/07/weekly-hmms-communities-laptops-linux/lenovo_keyboard_hu14456863796118609968.webp similarity index 100% rename from articles/2019/12/07/weekly-hmms-communities-laptops-linux/lenovo_keyboard_hu7103f81b0bac2c90aa84d23e3ec1b381_72090_752x461_resize_q75_h2_box.webp rename to articles/2019/12/07/weekly-hmms-communities-laptops-linux/lenovo_keyboard_hu14456863796118609968.webp diff --git a/articles/2019/12/14/weekly-hmms-advent-of-code-nginx-apis/index.html b/articles/2019/12/14/weekly-hmms-advent-of-code-nginx-apis/index.html index 854913a8..43d476f5 100644 --- a/articles/2019/12/14/weekly-hmms-advent-of-code-nginx-apis/index.html +++ b/articles/2019/12/14/weekly-hmms-advent-of-code-nginx-apis/index.html @@ -1,13 +1,21 @@ Weekly hmms: advent of code, NGINX, APIs | aviskase -

    Weekly hmms: advent of code, NGINX, APIs

    +14 Dec 2019
    +1 min

    Advent of code

    Last week I forgot to mention that I started solving the Advent of Code challenges. This is my first year, so I don’t even try to have elegant solutions. Old trusty Python, lists’ and dicts’ galore.

    The reason is simple: the goal is to go through all 25 days. Being consistent is not easy, and today I had to solve two days because of yesterday’s Christmas party.

    NGINX

    The news about police raids in NGINX office is at the same time surprising and not. Before there were VKontakte and Euroset; for some reason being honestly successful is not allowed in Russia.

    APIs

    Tom Johnson released a podcast with Arnaud Lauret. Even though it’s a tech writing perspective, I strongly recommend testers to check it out. Laments about not being able to influence design are fun to hear: it’s almost like Agile, DevOps, WhatNotOps missed out tech writers.

    Also, I found two interesting API related newsletters: “API Developer Weekly” and“Net API Notes”. If you look at the signup page for the first one, I bet you will notice that it doesn’t mention testers. I wonder, is it because their team follows MT-like approach and includes testers into dev set? Hope so.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/12/21/weekly-hmms-api-practices-yegor-bugaenko-culture/index.html b/articles/2019/12/21/weekly-hmms-api-practices-yegor-bugaenko-culture/index.html index 248ef8c2..1add8958 100644 --- a/articles/2019/12/21/weekly-hmms-api-practices-yegor-bugaenko-culture/index.html +++ b/articles/2019/12/21/weekly-hmms-api-practices-yegor-bugaenko-culture/index.html @@ -1,13 +1,27 @@ Weekly hmms: API practices, Yegor Bugaenko, culture | aviskase -

    Weekly hmms: API practices, Yegor Bugaenko, culture

    +21 Dec 2019
    4 mins

    API practices if you hate your customers

    A fun article from ACM Queue magazine about how to alienate customers from using your API. I like an “anti-tutorial” form, it is easier to recall bad patterns and behaviors.

    One such form I filled out required me to describe the application I planned to write. <…> @@ -35,6 +49,6 @@ it’s just a comedy, but these series are not marketed as a parody show. It’s a canon now. Which means, that obnoxious and demeaning conduct of the captain is considered ok in that universe. No one reprimanded her for causing an ecological disaster, and she never showed any remorse for the “idiot’s” death.

    I understand that showrunners are just money hoarders and want to have a comedy because it’s profitable. They -probably never consider how it affects existing canon.

    Still. I wonder, what this modern approach tells about our society?

    older +probably never consider how it affects existing canon.

    Still. I wonder, what this modern approach tells about our society?

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/12/26/lunchlearn-linting-openapi-description-docs/index.html b/articles/2019/12/26/lunchlearn-linting-openapi-description-docs/index.html index 40954716..8d56fadb 100644 --- a/articles/2019/12/26/lunchlearn-linting-openapi-description-docs/index.html +++ b/articles/2019/12/26/lunchlearn-linting-openapi-description-docs/index.html @@ -1,13 +1,12 @@ Lunch&Learn: linting OpenAPI description docs | aviskase

    Lunch&Learn: linting OpenAPI description docs

    +26 Dec 2019
    5 mins

    Last yearly company retrospective showed that we want to be better at knowledge sharing. One of the suggested formats is Lunch&Learn, kinda like Google’s Testing on the Toilet: short non-mandatory meeting during the lunchtime (30min) with shared recording later.

    I’ve started to use them for API related topics. Tools, terminology, and other small but important things that would be beneficial to most of the development team (FYI, testers are part of the dev team, duh). Also, it’s an opportunity to organize my brain, so I’ll be posting notes here for future reference. One thing though, these posts are not supposed to be comprehensive: there is only information applicable to my team!

    Validation vs. linting

    Anyone remotely familiar with coding knows that validators and linters are important for successful development. Shortly, validators check “does X conform to the standards/specification,” whereas linters complain about style and design issues. Usually, tools combine both in some way.

    Here I’ll cover linters for OpenAPI description documents. But you can use linting for everything, from obvious source code files to shell scripts or markdown documents.

    Why we need a linter for OpenAPI description docs

    We’ve started writing style guidelines for our APIs. Usually, I am that nitpicky person to review description docs; but I am lazy. I choose to automate myself out of the job. I don’t want to spend time checking for obvious style violations like capitalization or error definitions. And by the way, it would speed up the process.

    Because of that, our current requirements for the linter are:

    • easy to set up and run locally (so that devs and testers could use it without pain)
    • can be run via Jenkins
    • possible to customize with our own rules
    • support for OpenAPI 2
    • has a future: development is active and there is at least the possibility of OpenAPI 3 support

    Current implicit validation

    Right now we have description docs validation during integration tests, where we use bravado library for client generation. By default, it has some validation on doc load, and if the doc is invalid, bravado will complain. Internally it uses swagger_spec_validator. Here is a small script to show how it’s typical exceptional output looks like.

    import argparse
     from pathlib import Path
     from swagger_spec_validator import validate_spec_url
    @@ -54,7 +53,7 @@
       File "<...>/venv/lib/python3.7/site-packages/swagger_spec_validator/validator20.py", line 602, in validate_unresolvable_path_params
         raise SwaggerValidationError(msg)
     swagger_spec_validator.common.SwaggerValidationError: ("Path parameter 'tenantID' used is not documented on '/{tenantID}/administrators/'", SwaggerValidationError("Path parameter 'tenantID' used is not documented on '/{tenantID}/administrators/'"))
    -

    Also, bravado performs bits of contract testing: by default, it validates incoming responses against description docs.

    It serves us nice, but feedback is slow. At least we know when our spec is blatantly broken (beloved duplication of operationIds).

    Zally

    Zally is a linter from Zalando, which is famous for its open-sourced API guidelines.

    It ticks all our requirements except for the ease of setup: Zally is written in Kotlin. I was too lazy even to try running it. I’m sure it’s an excellent tool, but not for us.

    BTW, it has nice UI:

    Zally web UI with violations highlighting and links

    Speccy

    Welcome to JavaScript galore. Speccy and all next linters are run using node.js. Even though I’m not a fan of JS, it’s easy to use.

    So, back to Speccy. It was a popular tool once, but won’t work in our company:

    • no support for OpenAPI 2
    • is (was?) developed by WeWork. Ouch.

    IBM OpenAPI validator

    The next candidate is from IBM. Respectable origin. I like how it reports problems with the exact origins of errors:

    ➜ npx ibm-openapi-validator -e api.json
    +

    Also, bravado performs bits of contract testing: by default, it validates incoming responses against description docs.

    It serves us nice, but feedback is slow. At least we know when our spec is blatantly broken (beloved duplication of operationIds).

    Zally

    Zally is a linter from Zalando, which is famous for its open-sourced API guidelines.

    It ticks all our requirements except for the ease of setup: Zally is written in Kotlin. I was too lazy even to try running it. I’m sure it’s an excellent tool, but not for us.

    BTW, it has nice UI:

    Zally web UI with violations highlighting and links

    Speccy

    Welcome to JavaScript galore. Speccy and all next linters are run using node.js. Even though I’m not a fan of JS, it’s easy to use.

    So, back to Speccy. It was a popular tool once, but won’t work in our company:

    • no support for OpenAPI 2
    • is (was?) developed by WeWork. Ouch.

    IBM OpenAPI validator

    The next candidate is from IBM. Respectable origin. I like how it reports problems with the exact origins of errors:

    ➜ npx ibm-openapi-validator -e api.json
     npx: installed 244 in 5.108s
     
     errors
    @@ -78,6 +77,6 @@
      11602:26  error  no-$ref-siblings               $ref cannot be placed next to any other properties
     
     ...
    -

    You can either use Stoplight Studio or integrate with your IDE of choice to click lazily on an error to go to its source: I did with Sublime and SublimeLinter plugin, worked fine. PyCharm (and other IDEAs) are ok too.

    You can either use Stoplight Studio or integrate with your IDE of choice to click lazily on an error to go to its source: I did with Sublime and SublimeLinter plugin, worked fine. PyCharm (and other IDEAs) are ok too.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2019/12/26/lunchlearn-linting-openapi-description-docs/zally_hu89ad4f628f615cee6a6a367e8ea01c4e_97054_1314x695_resize_q75_h2_box_3.webp b/articles/2019/12/26/lunchlearn-linting-openapi-description-docs/zally_hu2682312627683805023.webp similarity index 100% rename from articles/2019/12/26/lunchlearn-linting-openapi-description-docs/zally_hu89ad4f628f615cee6a6a367e8ea01c4e_97054_1314x695_resize_q75_h2_box_3.webp rename to articles/2019/12/26/lunchlearn-linting-openapi-description-docs/zally_hu2682312627683805023.webp diff --git a/articles/2020/02/01/hmms-january/index.html b/articles/2020/02/01/hmms-january/index.html index ac5cfdec..832c367d 100644 --- a/articles/2020/02/01/hmms-january/index.html +++ b/articles/2020/02/01/hmms-january/index.html @@ -1,10 +1,15 @@ Hmms: January | aviskase -

    Hmms: January

    +1 Feb 2020
    3 mins

    Hi! Weekly hmms are transformed into monthly hmms, mostly because writing mandatory weekly posts is too cumbersome and leaves no time to do more thematic writing. Of course, there are other reasons too. Almost all winter holidays I had a cold (TWICE!) and being ill isn’t the best motivation ever.

    Knowledge bits

    I didn’t read or watch much, but some of the interesting findings were:

    Work things

    The company I work for was recognized as one of Montreal’s top employers. @@ -19,6 +24,6 @@ and preferable MMO: even though I prefer to play by myself, there is something comforting in seeing real people around and occasionally helping others (or receiving help). The perfect choice is The Elder Scrolls Online. The other recommended game is Star Wars: The Old Republic, but it’s too much Star Wars today. And TES has great lore. -Even though I never played Skyrim or Morrowind, TES wiki is an awesome read for people interested in sci-fi & fantasy genres.

    FYI, I play on the PC NA server ;)

    older +Even though I never played Skyrim or Morrowind, TES wiki is an awesome read for people interested in sci-fi & fantasy genres.

    FYI, I play on the PC NA server ;)

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/02/07/api-testing-in-python-requests-vs-bravado/index.html b/articles/2020/02/07/api-testing-in-python-requests-vs-bravado/index.html index 8340258b..b3370df6 100644 --- a/articles/2020/02/07/api-testing-in-python-requests-vs-bravado/index.html +++ b/articles/2020/02/07/api-testing-in-python-requests-vs-bravado/index.html @@ -1,11 +1,22 @@ API testing in Python: requests vs bravado | aviskase -

    API testing in Python: requests vs bravado

    +7 Feb 2020
    +7 mins

    This article is written as a result of collaboration with TestProject. While many of you know me as a GUI-driven tools hater, that’s just my preference, so if something works for you and your company, that’s the only thing that matters. There are no best practices and there are no best tools for everyone.

    What I really admire the TestProject team for is their strategy of creating a knowledge-sharing community. @@ -139,6 +150,6 @@ but usually making real fuzzy tests is easier with general-purpose HTTP libraries.

  • Not your code. There could be bugs. Be careful when selecting a library, it’s better to be open source with fresh contributions. Also, pay attention to supported specifications: bravado does not support OpenAPI 3.0 =(
  • The world of API related tools and libraries is enormous. Some people prefer GUI-driven tools like Postman or TestProject, others prefer to code. Next time you’ll be writing automation, don’t feel restricted to common generic -libraries and consider different alternatives.

    older +libraries and consider different alternatives.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/03/08/hmms-february/index.html b/articles/2020/03/08/hmms-february/index.html index 9c144474..6d79edfb 100644 --- a/articles/2020/03/08/hmms-february/index.html +++ b/articles/2020/03/08/hmms-february/index.html @@ -1,13 +1,33 @@ Hmms: February | aviskase

    Hmms: February

    +8 Mar 2020
    2 mins

    It’s March already, time to summarize February!

    APIs trainings

    I did a small company-wide training about APIs. Here is the main deck and supplementary deck left from dev lunch&learn. Anyone is free to use it and if you have questions just ping me somewhere. They don’t have notes, so can be pretty @@ -24,6 +44,6 @@ while one is chill and mildly sarcastic about it, the other keeps being almost aggressive. The biggest problem with the second community for me is that the most vocal followers act as non-debatable intellectuals who paint everything in absolutes and this behavior is supported by their leaders. Well. You never know until you -dive in.

    older +dive in.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/03/31/hmms-march/apis_meme_huca170f4c56b9fffaac286dc25e85d456_94188_735x500_resize_q75_h2_box.webp b/articles/2020/03/31/hmms-march/apis_meme_hu13229097982380154489.webp similarity index 100% rename from articles/2020/03/31/hmms-march/apis_meme_huca170f4c56b9fffaac286dc25e85d456_94188_735x500_resize_q75_h2_box.webp rename to articles/2020/03/31/hmms-march/apis_meme_hu13229097982380154489.webp diff --git a/articles/2020/03/31/hmms-march/index.html b/articles/2020/03/31/hmms-march/index.html index 51fb4515..00800172 100644 --- a/articles/2020/03/31/hmms-march/index.html +++ b/articles/2020/03/31/hmms-march/index.html @@ -1,20 +1,76 @@ Hmms: March | aviskase

    Hmms: March

    +31 Mar 2020
    +2 mins

    Coronavirus-free edition!

    Top X videos and readings

    Meme: APIs, APIs everywhere

    Another chose to build their own API gateway because no offerings existed that would operate in their preferred Windows-based server environment. @@ -30,6 +86,6 @@ at least one day a week, often more. One would say I have immense amount of tips and tricks to share, but I don’t. Because I’ve never been accustomed to the office work in the first place. I had to learn how to work NOT from home.

    All the sad things aside, it’s curious how much this experience can change companies in the positive way. Some didn’t even consider before that their work can be done remotely. Perhaps we will see a surge of remote-friendly -and/or flexible hours positions (which are rare at the moment)?

    older +and/or flexible hours positions (which are rare at the moment)?

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/index.html b/articles/2020/04/22/using-insomnia-for-api-exploration/index.html index f439ae91..28b44679 100644 --- a/articles/2020/04/22/using-insomnia-for-api-exploration/index.html +++ b/articles/2020/04/22/using-insomnia-for-api-exploration/index.html @@ -1,33 +1,35 @@ Using Insomnia for API exploration | aviskase -

    Using Insomnia for API exploration

    +22 Apr 2020
    3 mins

    One of the tools I use almost daily is Insomnia. It’s a great alternative to the P-everyone-knows-that-one. Insomnia is easy to use on Linux, has plugins, and UI is clean and simple.

    Let me show you some basic features. We will use OpenWeatherMap API.

    Workspaces

    Workspaces are collections of thematically combined requests. Some of my workspaces are service-specific, while others contain everything related to the particular client use case or event (i.e., cross-service).

    Our first examples: get current weather and forecast for Montreal. OpenWeatherMap API requires an API key, -so you need to add it to the query parameters for each request.

    Insomnia: basic interface

    Environments

    Too much duplication, isn’t it?

    Welcome to environments. Insomnia supports multi-layered variable assignments:

    • Base environment: values are accessible regardless of which environment is selected.

    • Sub environments: you can create as many as you want. Especially useful for testing APIs on different servers.

    • Folder-level environment: requests can be grouped by folders and sometimes it makes sense to assign specific values +so you need to add it to the query parameters for each request.

      Insomnia: basic interface

      Environments

      Too much duplication, isn’t it?

      Welcome to environments. Insomnia supports multi-layered variable assignments:

      • Base environment: values are accessible regardless of which environment is selected.

      • Sub environments: you can create as many as you want. Especially useful for testing APIs on different servers.

      • Folder-level environment: requests can be grouped by folders and sometimes it makes sense to assign specific values to all requests in the folder.

      Ok, so for this API let’s do like this. https://api.openweathermap.org/data/2.5 can go into base, -because we have access only to one server.

      Insomnia: base environment

      An API key is perfect for the sub environment.

      Insomnia: sub environment

      “Montreal” is stored as a folder-level value, just for the sake of example.

      Insomnia: folder-level environment

      Then, our requests will look like (use {{var_name}} to access variable):

      Insomnia: requests with variables from environments

      You might notice that we still have to fill out the API key query parameter for each request. Kinda boring. +because we have access only to one server.

      Insomnia: base environment

      An API key is perfect for the sub environment.

      Insomnia: sub environment

      “Montreal” is stored as a folder-level value, just for the sake of example.

      Insomnia: folder-level environment

      Then, our requests will look like (use {{var_name}} to access variable):

      Insomnia: requests with variables from environments

      You might notice that we still have to fill out the API key query parameter for each request. Kinda boring. That’s where plugins can help, in this case, it’s insomnia-plugin-defaults. Just go into Preferences > Plugins and type its name to install. -This plugin allows us to set default headers and/or query parameters in the environments.

      Insomnia: environment setup with query param defaults

      And now we can remove it from requests:

      Insomnia: requests with default query params

      Response querying

      The response for the forecast API is big. What if we want to check all returned values for +This plugin allows us to set default headers and/or query parameters in the environments.

      Insomnia: environment setup with query param defaults

      And now we can remove it from requests:

      Insomnia: requests with default query params

      Response querying

      The response for the forecast API is big. What if we want to check all returned values for weather descriptions? We can do it by using JSONPath response filtering.

      $.list[*].weather[*].description means ‘Get descriptions from each element of weather -array that is a property of elements in the list array’.

      Insomnia: querying response using JSONPath

      Generating values

      Sometimes we want to use random values. Insomnia has embedded +array that is a property of elements in the list array’.

      Insomnia: querying response using JSONPath

      Generating values

      Sometimes we want to use random values. Insomnia has embedded template tags for that, like timestamp and UUID. For other cases plugins come to the rescue yet again: insomnia-plugin-random uses Chance library -which gives you tons of options.

      Here is how to generate random latitude and longitude coordinates:

      Insomnia: random value generation

      Insomnia: usage of generated values in the request

      BTW, use Ctrl+Space +which gives you tons of options.

      Here is how to generate random latitude and longitude coordinates:

      Insomnia: random value generation

      Insomnia: usage of generated values in the request

      BTW, use Ctrl+Space shortcut for the fastest template tag autocomplete.

      Reusing response values

      Another commonly used feature is sending a request with values from the response of another request. -In Insomnia you should use Response template tag and JSONPath if you want to grab a value from the response body:

      Insomnia: pulling values from previous response

      Insomnia: request with pulled values

      Pretty simple, huh?

      There are other useful features and no one stops you from writing a plugin if you miss anything. But, s’il te plaît, -don’t try to turn it into a complex automation solution. That’s what programming languages are for.


      See also:

    older +In Insomnia you should use Response template tag and JSONPath if you want to grab a value from the response body:

    Insomnia: pulling values from previous response

    Insomnia: request with pulled values

    Pretty simple, huh?

    There are other useful features and no one stops you from writing a plugin if you miss anything. But, s’il te plaît, +don’t try to turn it into a complex automation solution. That’s what programming languages are for.


    See also:

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_10_hu25d0cfcc0e4bd98897b5c1a3118eb575_78802_1247x401_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_10_hu15660869340336541232.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_10_hu25d0cfcc0e4bd98897b5c1a3118eb575_78802_1247x401_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_10_hu15660869340336541232.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_11_hubc9562b6464e2e291fc3734b9c92956a_66487_1088x857_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_11_hu1520948915033833725.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_11_hubc9562b6464e2e291fc3734b9c92956a_66487_1088x857_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_11_hu1520948915033833725.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_12_huff2b9c149b03061dcf937123bbaa4b75_78338_1178x388_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_12_hu8855644404883462568.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_12_huff2b9c149b03061dcf937123bbaa4b75_78338_1178x388_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_12_hu8855644404883462568.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_1_huead573a8756e44bcf75d98811835f592_124055_1920x570_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_1_hu13571107800654859749.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_1_huead573a8756e44bcf75d98811835f592_124055_1920x570_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_1_hu13571107800654859749.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_2_hua4b6f39ac643996f900fee14788163f0_37132_1038x300_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_2_hu967323280867185681.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_2_hua4b6f39ac643996f900fee14788163f0_37132_1038x300_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_2_hu967323280867185681.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_3_hu726d4745e1ebe860d9fc791057f0b867_34864_1011x300_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_3_hu7176328840009260833.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_3_hu726d4745e1ebe860d9fc791057f0b867_34864_1011x300_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_3_hu7176328840009260833.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_4_hub445a22ecd8e9713ba2be35b1026d80b_33058_1137x313_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_4_hu18017720356901226146.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_4_hub445a22ecd8e9713ba2be35b1026d80b_33058_1137x313_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_4_hu18017720356901226146.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_5_hua0c381ec8ae72d32013b0fc09b58aeea_72498_1200x430_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_5_hu1308626106319942276.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_5_hua0c381ec8ae72d32013b0fc09b58aeea_72498_1200x430_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_5_hu1308626106319942276.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_6_hu2a51f86aff89648f713e07b2827abf03_48228_1021x459_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_6_hu13845563787324375993.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_6_hu2a51f86aff89648f713e07b2827abf03_48228_1021x459_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_6_hu13845563787324375993.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_7_hubab8ca579e73ea44132c477fceb39822_63604_1146x376_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_7_hu6986358383163437930.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_7_hubab8ca579e73ea44132c477fceb39822_63604_1146x376_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_7_hu6986358383163437930.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_8_hu749a941adcfe38452b5e14edfe466a64_109722_704x868_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_8_hu17979945490628766764.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_8_hu749a941adcfe38452b5e14edfe466a64_109722_704x868_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_8_hu17979945490628766764.webp diff --git a/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_9_hu42eb8436ef1f5b1e7af4f8a0c2589b0d_39735_1083x675_resize_q75_h2_box_3.webp b/articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_9_hu12381856641623832241.webp similarity index 100% rename from articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_9_hu42eb8436ef1f5b1e7af4f8a0c2589b0d_39735_1083x675_resize_q75_h2_box_3.webp rename to articles/2020/04/22/using-insomnia-for-api-exploration/insomnia_9_hu12381856641623832241.webp diff --git a/articles/2020/05/02/hmms-april/index.html b/articles/2020/05/02/hmms-april/index.html index 8d62ebba..64002f96 100644 --- a/articles/2020/05/02/hmms-april/index.html +++ b/articles/2020/05/02/hmms-april/index.html @@ -1,19 +1,30 @@ Hmms: April | aviskase -

    Hmms: April

    +2 May 2020
    2 mins

    Learning

    One of the good things to emerge during the corona times is more educational opportunities.

    For example, you can (and perhaps should) check the “Advanced Distributed Systems Design” course by Udi Dahan.

    Another way to satisfy knowledge thirst is to attend a virtual conference:

    • API The Docs hosts bite-sized virtual events with discussion opportunities.
    • AsyncAPI has finished already, but you can watch it on YouTube.
    • OnlineTestConf is soon. I’m particularly excited about “The Lessons we can Learn from the Aviation Industry” by Conor Fitzgerald.

    New font

    Any “IT person” should have a favorite monospaced font. Except for those weirdos who prefer non-monospaced. @@ -25,6 +36,6 @@ but it’s cool to have bits of your work visible and perhaps at some point serving others. My admiration for companies who published their guidelines in the same matter grew immensely: Adidas, PayPal, Heroku, Microsoft, Zalando, etc. It really helps to be able to see real artifacts and -not just generic NDA-sanitized dev blog posts.

    older +not just generic NDA-sanitized dev blog posts.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/06/02/hmms-may/index.html b/articles/2020/06/02/hmms-may/index.html index d1959229..cd060b46 100644 --- a/articles/2020/06/02/hmms-may/index.html +++ b/articles/2020/06/02/hmms-may/index.html @@ -1,13 +1,24 @@ Hmms: May | aviskase

    Hmms: May

    +2 Jun 2020
    2 mins

    This was a slow month in the sense of discovering new.

    I finished the “Advanced Distributed Systems Design” course. CQRS, DDD, messaging patterns: doesn’t sound like this course is in any way helpful for testers. Yet being just a tester is dull. Exploring such courses helps to diminish the hardest @@ -23,6 +34,6 @@ proper drivers for Linux. And it’s cheap =) Bad thing is that I’ve just upgraded to Ubuntu 20.04 with 5.4 kernel that is buggy and prevents these drivers from being installed. Oopsie. So, I’m waiting until 5.6 kernel is stable enough or 5.4 is fixed to fully dive into e-drawing. At the very least I’d like to draw doodles and diagrams with it, -and maybe, MAYBE, actually learn to draw.

    older +and maybe, MAYBE, actually learn to draw.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/06/18/robust-apis-are-weird/index.html b/articles/2020/06/18/robust-apis-are-weird/index.html index 1f5da396..708ad4b6 100644 --- a/articles/2020/06/18/robust-apis-are-weird/index.html +++ b/articles/2020/06/18/robust-apis-are-weird/index.html @@ -1,13 +1,12 @@ Robust APIs are weird | aviskase

    Robust APIs are weird

    +18 Jun 2020
    4 mins

    My first full-time API testing experience was for SOAP services. There you learn what XSD is. You learn to love it.

    Of course, you do! With server-side enabled validation based on a schema, you need not worry about stupid testing like checking what happens when you send 100 length string where expected maximum length is 50. Just make sure that XSD is correct.

    After that witchcraft, testing RESTish APIs feels like going back in time. To the very manual times. But then you learn about JSON Schema (RAML, OpenAPI, etc) and you are happy again! Yay, we can turn on server-side validation and shove off stupid testing again.

    The problem is that JSON and XML are different beasts. Assuming at face value that whatever is defined in schema should be blindly validated can be wrong.

    Let me explain why. And don’t worry, I did the same mistake.

    Here is a simple XML:

    <?xml version="1.0"?>
     <item>
       <name>Ring of the Wild Hunt</name>
    @@ -43,6 +42,6 @@
     float("blah") --> Exception
     

    Maybe there is a trimming in between too, so float(" 10 ".trim()) == 10.0.

    Maybe our casting is incredibly smart and can do things like float("ten") == 10.0!

    But I digress. The main point is that XML-based documents often go through casting mechanism. Validation fails only when casting could not be done.

    JSONs are not plain-text. They already have some rudimentary types visible just by looking at them. If you add JSON Schema on top and use any not overly robust validator, the behavior will be different.

    Because the simplest way to validate a JSON document is first to consume it with some common library that guesses types almost like we do: “does it have quotes around? string!” And only then, with already cast value, to compare its type with whatever is defined in a schema.

    (Yes, yes, I do oversimplify internals)

    x = parse(<json>) 
     assert typeof(x) == 'float'
    -

    then for:

    • "tugrik": 10 passes
    • "tugrik": 10.0 passes
    • "tugrik": "10" fails
    • "tugrik": "blah" fails

    Can you guess how really robust validators are different? Right! They make "tugrik": "10" to pass validation. Because they follow Postel’s law:

    Be conservative in what you send, be liberal in what you accept.

    While this statement is way more complex than puny little tester’s blog could handle, it’s important to know its existence and application. Following Postel’s law to any degree is a design choice.

    If your service is designed to handle weak input like "tugrik": "10" as a valid number, it’s not a bug. If you send additional properties in the request and a service ignores them without throwing 4xx error, it’s not a bug either. It can be very well a feature.

    And yes, there is definitely a way to do robustness wrong or overdo it. If you write a browser engine from scratch (why?), you’ll follow Postel’s law religiously. Another reason would be ensuring the maximum uptime of API clients. More successful requests they do and less work is required from them to connect to your product result in more profit.

    So, when you explore new API, make sure you understand how’s and why’s around the design before filing bugs “Achtung, Achtung, no validation!!!1”.

    then for:

    Can you guess how really robust validators are different? Right! They make "tugrik": "10" to pass validation. Because they follow Postel’s law:

    Be conservative in what you send, be liberal in what you accept.

    While this statement is way more complex than puny little tester’s blog could handle, it’s important to know its existence and application. Following Postel’s law to any degree is a design choice.

    If your service is designed to handle weak input like "tugrik": "10" as a valid number, it’s not a bug. If you send additional properties in the request and a service ignores them without throwing 4xx error, it’s not a bug either. It can be very well a feature.

    And yes, there is definitely a way to do robustness wrong or overdo it. If you write a browser engine from scratch (why?), you’ll follow Postel’s law religiously. Another reason would be ensuring the maximum uptime of API clients. More successful requests they do and less work is required from them to connect to your product result in more profit.

    So, when you explore new API, make sure you understand how’s and why’s around the design before filing bugs “Achtung, Achtung, no validation!!!1”.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/07/01/hmms-june/index.html b/articles/2020/07/01/hmms-june/index.html index 78bc5c64..e1a4a17c 100644 --- a/articles/2020/07/01/hmms-june/index.html +++ b/articles/2020/07/01/hmms-june/index.html @@ -1,16 +1,24 @@ Hmms: June | aviskase

    Hmms: June

    +1 Jul 2020
    +2 mins

    Time for the list of things I found interesting/amusing this month.

    Let’s start with the good news: OpenAPI 3.1.0 RC is out!. Webhooks and reconciliation with JSON Schema are the highlights of the version. I hope tooling support won’t lag this time.

    I really liked the article by Daniel Jarjoura on communications. For example, I didn’t know that crowd brainstorming is proved to be less effective.

    Cool little apps I was recommended:

    Positioning change

    And, finally, the regular column “I kinda don’t like testers.” This month I tracked all testers-centric communities and articles to determine how useful these sources of information are for me. The result was as expected: noise to signal ratio was appalling. I’m tired of:

    • Rehashing of the same ideas by the same authors for years.
    • Questions that will never pass Stack Overflow filter.
    • “Should testers learn to code?” debate specifically.
    • Being overly dramatic about the slightest differences in opinion. The schools of testing, yadda-yadda.
    • Over-glorifying the tester’s role and knowledge (common in certain groups).
    • Jokes and memes about us (testers) vs. them (devs) (typical amongst juniors and junior oriented resources).

    Thus, I made some decision:

    1. I left RST and MoT (and its abolished sister group) slack communities. Stayed only in the ABT, because it’s smaller and more enjoyable.

    2. Won’t hunt for any more testers-centric resources. Mind you, testers-centric ≠ testing-centric. Whatever I had in RSS + ABT podcast is more than enough.

    45 RSS feeds in “Testing” category

    3. Won’t save articles into reading queue “just in case”, if it’s obvious from the title and the first paragraph that the author is repeating themselves.

    4. Change “About me” page to be less role specific. I wrote it a long time ago.

    5. Do I contribute to the same noise? Perhaps. Can’t promise, but I’ll try not to. Obviously, these Hmms aren’t really original content, but written reflection is a great way to process information, sorry!

    P.S. the saddest YouTube’s recommendation

    YouTube recommended me to rewatch the opening sequence to the “Valerian and the City of a Thousand Planets”. The movie is forgettable, but this small part is a masterful tearjerker, especially now.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/07/01/hmms-june/rss_feeds_hu2a62ebfcb3418706af0af2941b59b141_14367_353x237_resize_q75_h2_box_3.webp b/articles/2020/07/01/hmms-june/rss_feeds_hu16719031399248944102.webp similarity index 100% rename from articles/2020/07/01/hmms-june/rss_feeds_hu2a62ebfcb3418706af0af2941b59b141_14367_353x237_resize_q75_h2_box_3.webp rename to articles/2020/07/01/hmms-june/rss_feeds_hu16719031399248944102.webp diff --git a/articles/2020/07/03/knowledge-management-tools/index.html b/articles/2020/07/03/knowledge-management-tools/index.html index a2f4df93..fe3fe2b9 100644 --- a/articles/2020/07/03/knowledge-management-tools/index.html +++ b/articles/2020/07/03/knowledge-management-tools/index.html @@ -1,13 +1,24 @@ Knowledge management tools | aviskase

    Knowledge management tools

    +3 Jul 2020
    +6 mins

    As a knowledge worker, I spend a lot of time searching and experimenting with approaches for knowledge capture and storage. Currently, I play with the new shiny app, so I figured it’s a perfect moment to systemize and reflect on tools and techniques I used until now.

    Handwritten

    I was more into handwriting during school and university, and there are still tons of notebooks back in Kazakhstan with course notes. I followed the same process and structure for extra studies like Coursera, and the most important thing I learned was that these notes were never ever opened and referenced later. Useless waste of stationery.

    One of the last examples from English&French practice:

    Old handwritten notebook

    Don’t get me wrong, I love stationery. There was a time when we visited the biggest stationery store almost every day after the classes to gawk on new cute notebooks and pens. If my handwriting wasn’t that atrocious, perhaps I’d be more into bullet journals and scrapbooks, but alas, it still doesn’t make them useful for anything else besides creativity outbursts.

    Now I use paper only for transient scribbles consumed right away or during the day, like this problem solving for advent of code.

    Untranslatable scribbles

    Though, I prefer Rocketbook, because it’s like a mini-whiteboard and I get to enjoy all fancy colors. Here is a note from API design brainstorming:

    Brainstorming in Rocketbook

    Digital

    If handwritten notes are useless, digital ones are too chaotic, because I experimented with too many formats.

    Non-structured

    The first iterations were a transposition of handwritten notes into digital via Google Docs.

    Notes in the Google Docs

    They are as ineffective as the real notebooks. Type once and forget.

    The direct opposite are plain text notes in Sublime with PlainNotes plugin. I use them almost every day for:

    • transient notes like “did this during the smoke test”
    • common copy pastes

    Sublime with PlainNotes

    Mind maps

    At some point I learned about mind mapping and tried almost all installable applications for that, including:

    • XMind: UX is great, but features in the free tier are meh, especially in the last versions. Likes to eat memory.
    • Freeplane: ugly. Very ugly. Unbearably ugly. But free, fast, and has tons of features.
    • Mindomo: my latest selection, not free, yet has feature no one else has (more on that later).

    As of now I have an assortment of mind maps in various formats, like Freeplane or XMind:

    XMind mind map overview

    I liked mind map for capturing phase, but for some inexplicable reason, I found them uncomfortable for referencing. They have the same fate as old course notes.

    Outliners

    After that realization I discovered outliners: think of them as a marriage between mind maps and bullet lists. You can follow the same tree-like structure and fold/unfold branches, yet with more capabilities for formatting and longer text.

    The most well advertised outliner is WorkFlowy, but I never tried it, because I’m an awful human being and prefer free stuff xD So the next best alternatives are Dynalist and Checkvist. Both are fine. There are hundreds of others, some of them are open source and/or crazy and/or vim-like outliners, but as UX junkie, I used those two the most. The reasons to stop were:

    • opposite of mind maps: hard to capture, easy to reference
    • web-based

    That’s why I moved to Mindomo, because it has a killer feature: switch between mind map and outline view.

    Notebooks

    Around the same time as I discovered mind mapping, I saw the need for having long form note storage. Yeah, yeah, Evernote rules the stage. I had it, but the web version became slower and slower, and there was no Linux client, so I played with others. It’s hard to remember their names though, I bet I tested more than a dozen.

    For example, Simplenote. Nice, but very basic.

    Or extremely Chinese WizNote.

    The last one ditched just a month ago was Joplin. It’s very good, I do recommend it: open source, supports markdown, has a web clipper and an Android app with WebDAV sync. If all you need is more or less suitable Evernote alternative, it’s ok. Spoilers: I needed more.

    Before we go to the last section, notable mentions:

    • LaTeX and RST formats: used them for a while, but Markdown is way easier and better supported.
    • OneNote: I heard it’s fine if you are Windows user. Well, I am not :)
    • Google Keep: perfect inbox for quick notes, shopping, and other lists that I need to access from the phone:

    Google Keep with transient lists

    Zettels

    Ok, that’s a wrong term, but it encapsulates what differs from common notebooks the best. Zettelkasten is a note-taking method that relies on linking. Overly simplified process is:

    • create basic and the smallest possible notes (one concept per note)
    • link notes with each other
    • group notes thematically

    There are different variations and similar methods, but all of them are based on ability to form concept maps (non-hierarchical storage). Checkout Andy Matuschak’s notes for example.

    The most crucial part is to trace links and backlinks for each note:

    • links: note A links to notes B, C, D
    • backlinks: note A is linked from notes E, F, J

    Usually it’s easy to add links, but cumbersome to add backlinks, because as soon as you link A to B you need to open B and add a link to A. That’s a wiki approach.

    There are apps that simplify the process, like Obsidian. It has other neat features: link visualization with a graph, note/file transclusion (aka “embedding”), and tagging.

    I’ve started slowly migrating my non-work notes there, and it’s hard. You get used to rigid hierarchies based on folders. You don’t have to stop using them, but it makes sense with approaches like @nickmilo’s (which I follow loosely).

    Non-text content

    Only digital:

    • Inkscape: my go-to drawing app since I’m better with vector graphics.
    • Krita: when I learn how to draw with my graphic tablet xD
    • Excalidraw: discovered via Obsidian’s Discord. Will use it for graphs.
    • LibreOffice Impress: not a drawing app, but for those situations when I need a presentation.
    • Shutter: screenshots with annotations, more than enough for everyday usage. Or ShareX when on Windows.
    • SimpleScreenRecorder for short screencasts. People who use GIF for that: burn in hell.

    Summary

    Let’s summarize. Handwritten notes:

    • use whiteboarding format (can easily “edit” by cleaning)
    • transient, not for long-term storage
    • for generating ideas or designs
    • for small tasks during the day

    One important exception is recipes. All are handwritten on small cards and stored in the box. The reason is to pull one out and stick to the fridge with a magnet. I don’t have much recipes anyway, so searching and storage aren’t a problem.

    For digital:

    • Mindomo when I really want a mind map. Something tells me I won’t.
    • Sublime PlainNotes for transient notes and copy pastes.
    • Google Keep for semi-transient lists and inbox.
    • Obsidian for anything else, except overly work related (I prefer to experiment with it more on the free tier before paying for commercial license).

    And for non-text content, imagine the entire section transcluded here ;)

    The last important bit: almost all my data is backed up in some way. Google Disk, Yandex.Drive, and OneDrive. I once lost a hard drive with my end-of-semester projects and 300GB of carefully collected music (which I never recovered). Make backups. And backup some backups.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/07/03/knowledge-management-tools/km_gdocs_hufa58e120375260f0cde684b8fe8fce22_58824_1304x381_resize_q75_h2_box_3.webp b/articles/2020/07/03/knowledge-management-tools/km_gdocs_hu17954551154731474462.webp similarity index 100% rename from articles/2020/07/03/knowledge-management-tools/km_gdocs_hufa58e120375260f0cde684b8fe8fce22_58824_1304x381_resize_q75_h2_box_3.webp rename to articles/2020/07/03/knowledge-management-tools/km_gdocs_hu17954551154731474462.webp diff --git a/articles/2020/07/03/knowledge-management-tools/km_keep_hub3cedfb4d79ea0809104fd94b53e170b_40871_920x261_resize_q75_h2_box_3.webp b/articles/2020/07/03/knowledge-management-tools/km_keep_hu7597548926287526888.webp similarity index 100% rename from articles/2020/07/03/knowledge-management-tools/km_keep_hub3cedfb4d79ea0809104fd94b53e170b_40871_920x261_resize_q75_h2_box_3.webp rename to articles/2020/07/03/knowledge-management-tools/km_keep_hu7597548926287526888.webp diff --git a/articles/2020/07/03/knowledge-management-tools/km_notes_hu6f30cc9bd7ddf0d65891d4c3af252265_77155_300x400_resize_q75_h2_box.webp b/articles/2020/07/03/knowledge-management-tools/km_notes_hu10671746300460593470.webp similarity index 100% rename from articles/2020/07/03/knowledge-management-tools/km_notes_hu6f30cc9bd7ddf0d65891d4c3af252265_77155_300x400_resize_q75_h2_box.webp rename to articles/2020/07/03/knowledge-management-tools/km_notes_hu10671746300460593470.webp diff --git a/articles/2020/07/03/knowledge-management-tools/km_oldnotes_hu966eabb7042cba590489bc15c71caa08_405591_1000x750_resize_q75_h2_box.webp b/articles/2020/07/03/knowledge-management-tools/km_oldnotes_hu13137737375096810539.webp similarity index 100% rename from articles/2020/07/03/knowledge-management-tools/km_oldnotes_hu966eabb7042cba590489bc15c71caa08_405591_1000x750_resize_q75_h2_box.webp rename to articles/2020/07/03/knowledge-management-tools/km_oldnotes_hu13137737375096810539.webp diff --git a/articles/2020/07/03/knowledge-management-tools/km_rocketbook_hueb28916bbd75b1bfd9acc9a71b4e419c_202967_1000x750_resize_q75_h2_box.webp b/articles/2020/07/03/knowledge-management-tools/km_rocketbook_hu7041490496210265153.webp similarity index 100% rename from articles/2020/07/03/knowledge-management-tools/km_rocketbook_hueb28916bbd75b1bfd9acc9a71b4e419c_202967_1000x750_resize_q75_h2_box.webp rename to articles/2020/07/03/knowledge-management-tools/km_rocketbook_hu7041490496210265153.webp diff --git a/articles/2020/07/03/knowledge-management-tools/km_sublime_hu11c88f6b4f8e4d4566211d83825cf277_32652_834x494_resize_q75_h2_box_3.webp b/articles/2020/07/03/knowledge-management-tools/km_sublime_hu4821790319132219107.webp similarity index 100% rename from articles/2020/07/03/knowledge-management-tools/km_sublime_hu11c88f6b4f8e4d4566211d83825cf277_32652_834x494_resize_q75_h2_box_3.webp rename to articles/2020/07/03/knowledge-management-tools/km_sublime_hu4821790319132219107.webp diff --git a/articles/2020/07/03/knowledge-management-tools/km_xmind_hu9c32adda7f2c091cd2c5509224b7043c_84994_564x394_resize_q75_h2_box_3.webp b/articles/2020/07/03/knowledge-management-tools/km_xmind_hu15548076838612635709.webp similarity index 100% rename from articles/2020/07/03/knowledge-management-tools/km_xmind_hu9c32adda7f2c091cd2c5509224b7043c_84994_564x394_resize_q75_h2_box_3.webp rename to articles/2020/07/03/knowledge-management-tools/km_xmind_hu15548076838612635709.webp diff --git a/articles/2020/08/04/hmms-july/index.html b/articles/2020/08/04/hmms-july/index.html index f94c560e..114781f0 100644 --- a/articles/2020/08/04/hmms-july/index.html +++ b/articles/2020/08/04/hmms-july/index.html @@ -1,13 +1,24 @@ Hmms: July | aviskase -

    Hmms: July

    +4 Aug 2020
    +2 mins

    Oops, I have nothing to share

    Since migrating to Obsidian for knowledge management, I’ve lost a track of interesting resources I read, because they are represented as atomic cross-referencing notes.

    Perhaps I should try to be more consistent and always write literature notes. Alas, my current graph looks like that (obviously, I didn’t have time to transfer older notes from other systems):

    Obsidian graph view as of July 2020

    In general, my topics of interests for this month were:

    • jobs to be done framework
    • jobs stories vs. user stories
    • characters vs. personas
    • API design approaches
    • relearning basic Lean & Kanban principles like value stream mapping or my beloved 3Ms

    No longer a tester

    My official title has changed to engineer. But don’t expect me to stop whining about testers’ community; I still talk to them xD

    Anyway, title change was a cumbersome thing. On one hand, I don’t care that much. As long as people know me and understand that I’m that weird glue person who just pokes into every process where waste is evident, I’m fine. On the other hand, title matters for stupid things like initial introduction to the new team or hierarchical structures. Would I prefer something less generic than “engineer” that may accidentally focus on “implementer” hat instead of the “analyst/designer”? Yup. Did I have imperfect titles before? Yup. So, whatever.

    First learnings from wearing implementer hat

    I am mostly Python coder, though I have some almost forgotten experience with C++ and Java. But now I need to work with Typescript/Node.js code and it’s been… interesting. The biggest problem coming from Python is that in JS there are a million ways to do the same thing which for me results in endless googling about what way is more idiomatic and recommended. The entire ecosystem around TS, JS, and Node seems to be more confusing. But, no arguments here, type system in TS is nuts. Like an additional language.

    I also enjoy working in Visual Studio Code, especially with its automagic connection inside docker containers. This blog is written in the PyCharm, but now I consider switching to VSCode because it has more markdown plugins.

    Another dev thingy: the blog was built using Travis CI. Recently I’ve switched to GitHub Actions, and it was fairly easy to do. I’d suggest to anyone who wants to learn DevOps-ish stuff to experiment on GitHub, since it’s all in one place and their docs are user friendly.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/08/04/hmms-july/obsidian_july_2020_hua4ff3e2cfd110b6ba07989c28888d4ad_49328_639x596_resize_q75_h2_box_3.webp b/articles/2020/08/04/hmms-july/obsidian_july_2020_hu8094199773841854266.webp similarity index 100% rename from articles/2020/08/04/hmms-july/obsidian_july_2020_hua4ff3e2cfd110b6ba07989c28888d4ad_49328_639x596_resize_q75_h2_box_3.webp rename to articles/2020/08/04/hmms-july/obsidian_july_2020_hu8094199773841854266.webp diff --git a/articles/2020/08/08/be-chicken/index.html b/articles/2020/08/08/be-chicken/index.html index 25f38015..1db34be0 100644 --- a/articles/2020/08/08/be-chicken/index.html +++ b/articles/2020/08/08/be-chicken/index.html @@ -1,19 +1,18 @@ Be chicken! | aviskase

    Be chicken!

    +8 Aug 2020
    +2 mins

    The article on API change culture uses a well-known “the chicken and the pig” fable as an inspiration:

    A Pig and a Chicken are walking down the road.

    The Chicken says: “Hey Pig, I was thinking we should open a restaurant!”

    Pig replies: “Hm, maybe, what would we call it?”

    The Chicken responds: “How about ‘ham-n-eggs’?”

    The Pig thinks for a moment and says: “No thanks. I’d be committed, but you’d only be involved.”

    I bet I won’t say anything new or profound, but it always weirds me out that the pig is considered somewhat more important than the chicken. Oh, right, because it must die to be useful.

    Well, I grew up in the 90s in Kazakhstan. Like many people we were relying on our village relatives for food, so ham and egg production is not an elegant metaphor but a real-life experience for me.

    Growing pigs is a slow process with a big one-time payoff. It’s not free: you need to feed them and clean the place. And you’d better have a pair of pigs so that the production could be at least somewhat renewable. One committed pig is a splendid feast in the short term and hunger in the long term. Don’t forget an emotional cost: pigs don’t die by themselves. You will hear it. You will smell it.

    Chicken produces eggs continuously. You don’t even need a rooster. Feeding is less involved, and you may practice a free-range approach to optimize costs. With a rooster, eggs can be fertilized, and production might grow even more.

    Thus, in my opinion, chicken-style contributors are better for a project. They bring sustainability. They create small eggs incremental improvements. They are the force of kaizen.

    Whereas pigs are one-time Big Bang doers. An external contractor who came in, said you need to redo the entire workflow, got their paycheck, and left. That new employee who said: “let’s rewrite the system from scratch!”. Don’t get me wrong, sometimes big changes are healthy and bring value, but they are also riskier. You need to have a bunch of chickens to support the process.

    Another possible consequence for pigs is burnout. You did something big and you can’t do anything else anymore. Oops.

    I often gravitate towards pig’s tendencies since it’s easier. It’s also better for CV and performance evaluations, isn’t it? ;) But in the end, as some say: we prefer being generalists; the same way I’d say: let’s be chickens who oink occasionally.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/08/20/postmortem-borking-ubuntu-again/index.html b/articles/2020/08/20/postmortem-borking-ubuntu-again/index.html index bdb949f9..d35ed57f 100644 --- a/articles/2020/08/20/postmortem-borking-ubuntu-again/index.html +++ b/articles/2020/08/20/postmortem-borking-ubuntu-again/index.html @@ -1,19 +1,15 @@ Postmortem: borking Ubuntu (again) | aviskase

    Postmortem: borking Ubuntu (again)

    +20 Aug 2020
    +4 mins

    Since I had to deal with an incident recovery this week, I thought why not use it for practicing writing postmortems?

    Postmortem is an excellent way to reflect and learn from disasters. Check Google’s SRE book for more information.

    So, let’s begin.

    Summary. On Tuesday at roughly 02:30 I wasn’t able to log into Ubuntu: GNOME crashed before being able to display login screen.

    Impact. Had to do a clean Ubuntu installation, most of the workday time lost.

    Root causes. Incorrect and incomplete purge of accidental upgrade to Pop!_OS. The upgrade itself was cased by adding ppa:system76/pop and not noticing that it contains all the packages that makes Pop!_OS a different distributive.

    Action items:

    • Buy an additional external drive and setup regular /home backup procedure.
    • Document all necessary tweaks for running Ubuntu on Dell XPS 15.

    Lessons learned:

    • Always use separate partition for /home. Currently Ubuntu recommends otherwise, that’s why I didn’t make it.
    • Don’t ever upgrade or purge in the middle of the night (duh!).
    • Pay attention to PPA’s content.
    • One big external drive per household is a bottleneck.

    Timeline: (approximate)

    1. Saturday. While exploring shiny new themes for Ubuntu I stumbled upon Pop theme.
    2. Saturday. Since I prefer using PPA to installing from source, I added ppa:system76/pop repository and installed pop-theme.
    3. Monday 20:00. Ubuntu showed notification about available updates. I didn’t check it and simply agreed.
    4. Monday 23:00. Rebooted into Windows.
    5. Tuesday 01:30. Rebooted into Ubuntu to leave it ready for the next work day. Noticed that dual monitor settings and other customizations were gone.
    6. Tuesday 01:40. Checked apt, it showed a warning that some GNOME packages hadn’t been upgraded properly. Thus, I decided to rerun sudo apt upgrade manually.
    7. Tuesday 01:50. System suggests rebooting (accepted).
    8. Tuesday 02:00. After rebooting it became obvious that it’s no longer pure Ubuntu. Several places showed that it’s now Pop!_OS.
    9. Tuesday 02:10. Googled for how to downgrade back to Ubuntu (Pop!_OS lacks certain features). Didn’t read much and used the advice to run sudo ppa-purge ppa:system76/pop (was sleepy).
    10. Tuesday 02:20. Purge threw exception in middle and screwed up the system, GNOME in particular.
    11. Tuesday 02:30. After rebooting I was not able to login into UI. Root shell recovery mode worked.
    12. Tuesday 09:00. Spent time researching steps for fixing, decided it would take more time than reinstalling the system.
    13. Tuesday. Booted from LiveUSB. Discovered that there was no separate /home partition.
    14. Tuesday. There were no recent backups, so I had to do one manually. The external hard drive at the moment didn’t have enough space, and I needed to move data from it to other computer first.
    15. Tuesday. After cleaning up external drive I attempted to create tar archives with permission preservation. I went with two archives, one for VMs, another for everything else in /home.
    16. Tuesday. VMs’ archive creation failed several times. Had to split it in more archives based on directories.
    17. Tuesday. Copied all archives to external drive and checked it on the other computer. Archives for VMs were in the subdirectory and the system failed to open it (this computer has half-broken Manjaro, can’t find time to fix it).
    18. Tuesday. Copied VMs’ archive to USB drive. Checking failed, because I forgot that this drive had ext4 format.
    19. Tuesday. Tired and not wanting to bother with proper permissions setup, I formatted USB drive to NTFS, copied archives again, checked that they were accessible.
    20. Tuesday. Created separate partitions for / and /home and reinstalled Ubuntu.
    21. Tuesday. Transferred backups to the fresh system, unpacked, installed necessary apps.
    22. Tuesday. Checked that dual-boot Windows was still ok after all manipulations.

    I’ve been using Ubuntu, Fedora, or ElementaryOS since early university. You’d think I’ll have enough experience and caution not to do stupid things like purging GUI packages while still being in GUI ;) But on the other hand, I always had a separate /home partition and got used to rapidly recover or change distributions. Even though it sounds like a typical Windows user behavior, I see one important difference: generally, I do know what got broken. I did it myself. I’m just not patient enough to fix all the problems ¯\_(ツ)_/¯

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/08/30/remembering-javascript-and-typescript/index.html b/articles/2020/08/30/remembering-javascript-and-typescript/index.html index 6d4ae7e1..b3f23391 100644 --- a/articles/2020/08/30/remembering-javascript-and-typescript/index.html +++ b/articles/2020/08/30/remembering-javascript-and-typescript/index.html @@ -1,13 +1,12 @@ Remembering JavaScript and TypeScript | aviskase

    Remembering JavaScript and TypeScript

    +30 Aug 2020
    +2 mins

    Until recently, I had been coding mostly in Python. I’m not an expert, but have a fair understanding of PEPs and practices.

    But now I work with JavaScript/TypeScript project, and my knowledge is definitely lacking. I was following these only in the university, when there was still a chance that I had to do web development. I distinctly remember the moment of downloading “You Don’t Know JS: this & Object Prototypes” book and thinking “nope, that’s it, I don’t want to deal with such language.”

    Anyway, I’m in dire need of relearning JS and TS. After experience with Python I know better how important it is to lint, follow a style guide, and type, type, type it all. For learning I prefer books and articles, but also noticed growing appreciation for videos. Yes, they are less efficient, longer, but you can use them during some “brainless” time and revisit interesting topics afterwards in a more thorough fashion.

    Here is the list of resources I’m using now:

    On the side note, I think I’ll be migrating this blog from Pelican to Hugo (learning Go, why not) or something JS-y for a nice added practice. Though, the actual reason is that it’s built with an older Pelican version, which is very slow, so I have to spend time on either upgrading or migrating anyway.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/09/01/hmms-august/index.html b/articles/2020/09/01/hmms-august/index.html index fa21c747..f469498b 100644 --- a/articles/2020/09/01/hmms-august/index.html +++ b/articles/2020/09/01/hmms-august/index.html @@ -1,13 +1,12 @@ Hmms: August | aviskase

    Hmms: August

    +1 Sep 2020
    +2 mins

    This month’s hmms were supposed to be easy peasy to do: just grab all new notes created in Obsidian, curate a bit, and, voilà, done. I even wrote a small python tool.

    Yeah, right. I keep forgetting that it’s almost impossible to get file’s creation date on Linux. There is a hacky way for ext4, but in my case it didn’t work: according to the script, almost all notes were created on Aug 18 o_O. Ok, lesson learned, now my notes will have yaml front matter with creation date =(

    After sifting through files manually, looks like these were the resources that grabbed my attention the most:

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/09/15/conference-notes-asc-2020/index.html b/articles/2020/09/15/conference-notes-asc-2020/index.html index 1865e0ae..8ba68a80 100644 --- a/articles/2020/09/15/conference-notes-asc-2020/index.html +++ b/articles/2020/09/15/conference-notes-asc-2020/index.html @@ -1,13 +1,9 @@ Conference notes: ASC 2020 | aviskase -

    Conference notes: ASC 2020

    +15 Sep 2020
    +4 mins

    OpenAPI Initiative’s API specifications conference (ASC) was the first payed event I’ve attended. Usually I watch free online events or past videos available on YouTube or Vimeo, but this time I decided to bite my inner Scrooge McDuck since there were relevant topics and price was reasonable.

    And I didn’t regret it! I would love to attend offline or online future event: community is golden. There were tons of activity and discussions. The obvious example was day two keynote where the discussion in the chat was almost (if not more) as active as the streamed panel discussion.

    The recurring theme in all breakout sessions was API governance. I found it funny that many participants said that they try to avoid the word “governance” because it scares or alienates developers. In the case of my current company, all our work is about giving customers means to do information governance. And while we certainly need to become better at governing some of our own processes, at least we’re not scared of the word.

    Talks that caught my attention (aside from keynotes):

    • Bridging Systems and Subcultures: A Swagger Origin Story - Zeke Sikelianos, GitHub
      • Was distracted by work for the first half of talk, so I’d want to rewatch it.
    • Communicating Warning Information in HTTP APIs - André Cedik, shipcloud.io
      • Schedule conflict; definitely to watch later.
    • Open APIs Wide Open - David Biesack, Apiture
      • Explains the power of x- in OpenAPI definition docs.
    • Managing API Specs at Scale - Jay Dreyer, Target Corporation
      • Inspiring to see how big companies govern their APIs; and you know, that’s not a rocket science, someone just have to do it!
    • From 0 to OpenAPI: Describing a 10-year old API with OpenAPI @ GitHub - Marc-André Giroux & Andrew Hoglund, GitHub
      • Schedule conflict; definitely to watch later.
    • Going AsyncAPI: The Good, The Bad, and The Awesome - Ben Gamble, Ably
      • I expected a bit more in depth explanation of practical usage for this specification, but it was still useful.
    • JSON Schema At Home in the OpenAPI Specification - Core concepts, Vocabularies, and Drafts for 2020 - Ben Hutton, JSON Schema
      • Rewatch and rewatch and rewatch: OpenAPI and JSON Schema are going to be compatible soon (yay!)
    • Create Delightful SDKs from OpenAPI - Lorna Mitchell, Vonage
      • In short, do not ship auto-generated clients. It’s like… deploying auto-generated servers.
    • The Augmented API Design Reviewer - Arnaud Lauret, Natixis
      • I’m a fan of Arnaud: his book sits on my table and his presentations are probably the best I’ve ever seen (visually and informationally).
    • The Vocabulary of APIs: Adaptive Linting for API Style Detection and Enforcement - Tim Burks, Google Nicole Gizzo, Google
      • Schedule conflict; definitely to watch later.
    • Contract as Code as Contract: Using Rust to Unify Specification and Implementation - Adam Leventhal & David Pacheco, Oxide Computer Company
      • Schedule conflict; definitely to watch later.
    • Contracts for Collaboration Driven Development - Alianna Inzana, SmartBear
      • Schedule conflict; definitely to watch later.
    • Get Rid of CRUD: Revealing Intent With Your API - Michael Brown, Microsoft
      • The talk that got me into the conference in the first place: this is what part of our development team is working on right now. Kudos to Michael for explaining me bits of it before the conference!
    • Don’t Make It Hard for Us! What Makes a “Good” API? - Matthew Adams & Carmel Eve, endjin
      • Good conversational session for chat, felt almost like a panel.
    • API Specification and Microservices Communication Patterns with gRPC - Kasun Indrasiri, WSO2 & Danesh Kuruppu, WSO2
      • You may have noticed it already, but even though the conference was organized by OpenAPI Initiative, all types of specifications were covered and gRPC is one of them.

    Other stuff to mention:

    • Got my first conference t-shirt! It’s even the right size xD
    • There were some technical hiccups, the first day I missed starting several minutes in each session.
    • Surprisingly, there were several people from Montreal and Quebec. Local testing community seems to be way less active.

    Oh, right, my usual rubric, whining about testers:

    1. Amusing (or not) observation: there was no “tester” option in the event registration form. Developers, managers, marketing, DevOps, whatever. But no testers. Oops.
    2. When people talk about API governance, they usually mention developers, product managers, tech writers. But almost never testers. I wonder why?
    3. Gotcha, no whining here! I’ve met a like-minded tester! She was quite active, and I was able to deduce that she was a tester just by the questions she posted. Can’t deny, there is a certain “world view” you get by wearing this hat long enough. The despair.
    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/10/04/from-pelican-to-hugo/index.html b/articles/2020/10/04/from-pelican-to-hugo/index.html index 047f7341..a5df29eb 100644 --- a/articles/2020/10/04/from-pelican-to-hugo/index.html +++ b/articles/2020/10/04/from-pelican-to-hugo/index.html @@ -1,16 +1,25 @@ From Pelican to Hugo | aviskase

    From Pelican to Hugo

    +4 Oct 2020
    4 mins

    As I mentioned in one of the last articles I was planning to migrate this blog from Pelican to other static site generator.

    Why and where

    Reasons:

    • Pelican got a new major version 4, which required some migration work on my side either way.
    • When I’m fixing spelling and style I prefer checking on rendered local site and not raw markdown. But live reload became too slow and frustrating (every change took approximately 1-3 minutes).
    • I just love to switch gears occasionally. This blog was originally built with Jekyll (until mid-2019).

    So, I opened StaticGen to check what was trendy. My final choice was between Gatsby and Hugo.

    GatsbyHugo
    JS-based, good for enforcing much-needed JS deep diveGo-based, simply cool.
    Very flexible, tons of plugins, data pulling via GraphQLSomewhat flexible. Still not sure about plugins.
    Praised for fast loading, especially if you have JSFast building. Extremely fast building.
    Often used for API dev portals

    As you have guessed by the title, I went with Hugo because:

    • I don’t need JS on the site itself
    • I’m more interested in fast builds than loading
    • I heard that Go is the dumbest language in the world, might as well experience some of it xD

    Migration shenanigans

    Pelican and Hugo have a very different template format. Rewriting took most of the time, but as you can see, there are no visual changes. I also had to write Atom feed template almost from scratch and test it to make sure that id’s are the same and the whole feed won’t be completely regenerated (like it happened the last migration.)

    But Hugo templates are way more flexible than Pelican’s. What I had to do with plugins was achieved with core features. I also fell in love with shortcodes and markdown render hooks which allow further customizations. For example, I used to have a Pelican plugin that detects links to external resources and adds a special external CSS class to them. Now the whole plugin is replaced with one link render hook that I can easily customize as I want later:

    <a href="{{ .Destination | safeURL }}"{{ if strings.HasPrefix .Destination "http" }} class="external"{{ end }}>{{ .Text | safeHTML }}</a>
    -

    Second problem was the markdown sources. They had special Pelican front matter format, not compatible with any supported by Hugo (YAML, TOML, JSON, Org). Yikes. Same for !!! note admonition syntax, kbd auto-detection (all replaced by shortcode), and internal links. I don’t have too much content, so most of the work was done via grep and manually replacing stuff.

    Third problem was images. The simple solution would be to set them all to static folder, but it’s not a recommended approach because you wouldn’t be able to use image processing features. Thus, you should go with assets or page bundles. I prefer bundles because they create a clear distinction where each image belongs to.

    Changes to the site

    While I was trying to preserve as much as I could, there are some intentional changes:

    • No more tagging and categories. I never used them; when I needed to find anything, I just used search. Perhaps it will also force me to make titles more explicit.
    • No Google tag (aka analytics). While it had been respecting all possible “Do Not Track” settings, I ditched it completely. At first, it was fun to watch graphs, incoming sources, and play with smart goals, but after a while I stopped caring. Search console is more than enough.
    • No Disqus comments. Not much value in them anyway (maybe I should actually use Twitter? LOL.)

    Results

    I’m quite happy with the migration. It is blazing fast as advertised: GitHub action build went from 2-3 minutes down to 30 seconds. And local live reload is faster than it takes to turn my head to the browser tab.

    There is only one problem left. I used img shortcode to generate responsive images, but for some reason on my site browsers select too low resolution. I tried to understand why yet couldn’t: I had less trouble hacking my way through GnuCash reporting in Scheme (which I don’t know) than trying to fix the damn CSS. Current style sheet was borrowed from some theme and reworked over time, but it probably doesn’t follow modern best practices. Hugo documentation introduced me to Tailwind CSS and PostCSS; and while I hate CSS, the time for redesign has come.

    Second problem was the markdown sources. They had special Pelican front matter format, not compatible with any supported by Hugo (YAML, TOML, JSON, Org). Yikes. Same for !!! note admonition syntax, kbd auto-detection (all replaced by shortcode), and internal links. I don’t have too much content, so most of the work was done via grep and manually replacing stuff.

    Third problem was images. The simple solution would be to set them all to static folder, but it’s not a recommended approach because you wouldn’t be able to use image processing features. Thus, you should go with assets or page bundles. I prefer bundles because they create a clear distinction where each image belongs to.

    Changes to the site

    While I was trying to preserve as much as I could, there are some intentional changes:

    Results

    I’m quite happy with the migration. It is blazing fast as advertised: GitHub action build went from 2-3 minutes down to 30 seconds. And local live reload is faster than it takes to turn my head to the browser tab.

    There is only one problem left. I used img shortcode to generate responsive images, but for some reason on my site browsers select too low resolution. I tried to understand why yet couldn’t: I had less trouble hacking my way through GnuCash reporting in Scheme (which I don’t know) than trying to fix the damn CSS. Current style sheet was borrowed from some theme and reworked over time, but it probably doesn’t follow modern best practices. Hugo documentation introduced me to Tailwind CSS and PostCSS; and while I hate CSS, the time for redesign has come.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/10/18/blog-redesign-phase-1/index.html b/articles/2020/10/18/blog-redesign-phase-1/index.html index 05a559b3..76c718f8 100644 --- a/articles/2020/10/18/blog-redesign-phase-1/index.html +++ b/articles/2020/10/18/blog-redesign-phase-1/index.html @@ -1,15 +1,27 @@ Blog redesign: phase 1 | aviskase

    Blog redesign: phase 1

    +18 Oct 2020
    +3 mins

    After migrating to Hugo, I decided to do a redesign to freshen up colors and introduce Hugo Pipes.

    Oh boi. First phase took the entire week.

    Planning

    For planning and progress tracking, I used a GitHub project. This is a basic Kanban board with two “To do” columns per phase. The first phase was about making a base dirty skeleton, whereas the second phase will add bells and whistles.

    GitHub project’s cards can have automated transitions between columns, for example, if you close the issue, it will go to “Done.” Unfortunately, when you work from branch, issues won’t be closed by commits until you merge the develop branch in to the main one. In my case the main branch is used for output hosting, so redesign branch won’t be merged there, thus, I had to close issues by hand. Meh.

    R&D

    From the Hugo docs I learned about Tailwind CSS and PostCSS. Tailwind CSS is kinda like Bootstrap, but without imposed styling; basically, a Lego constructor of utility classes. PostCSS is a tool for transforming CSS with various plugins like LESS/Sass-style syntaxes, autoprefixer, purger, etc. This repo and this one show how to integrate those tools into Hugo.

    Design inspiration came when I stumbled upon Joshua Comeau’s blog. Yeah, I’ve imitated a lot of its typography and color choices xD It also provided some tutorials, for example, how to style ordered lists. I spent some time trying to find a similar free humanist sans-serif font, Nunito seems close, readable, and has Cyrillic characters (just in case). As for a monospaced font, the choice was obvious: JetBrains Mono.

    Tailwind CSS has a plugin for typography; I didn’t use it, but copied 18rem based margins, line-heights, and font sizes for some elements.

    For layout I used grid and flexbox. Last time I did any CSS these two weren’t a common thing; you had to do a million of div’s with float’s and other weird things. I also wanted to use grid for minor bleeding elements how it was described here, but I didn’t like the result: vertical margins don’t collapse when you apply grid to child elements.

    BTW, raindrop.io helped tremendously with gathering links and sources. I completely migrated there from Pocket.

    It has a similar “save for later” experience, but also supports collections and comments. For example, for this project I created a collection “blog redesign” and added a comment for each link saved there to remember later why it was saved in the first place.

    Additional resources:

    Next steps

    Basic sections for next work are:

    • Bringing back old features: external vs. internal links, older/newer links.
    • New features: dark theme, automatic generation of Open Graph image, header-level links.
    • Cleaning up: splitting CSS into components, using plugin for font-face generation, gathering all hard-coded values into variables.
    • Fixing bugs: display of code elements in links and headers (yup, you can spot it in this article) and others.

    I also think about ditching Tailwind CSS at this point. It’s great for rapid prototyping, but it quickly became a mess of styling split between inline classes and the style sheet.

    And one more thing. I no longer like the selected color palette, lol. Links are too bright, block quotes are weird, the header is too cheesy. People say there are 2 hard problems in computer science: cache invalidation, naming things, and off-by-1 errors. I think “choosing colors” is the “naming” equivalent in design.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/10/25/automap-api-operation-handlers-in-components-based-project/index.html b/articles/2020/10/25/automap-api-operation-handlers-in-components-based-project/index.html index 9afba82d..353028d2 100644 --- a/articles/2020/10/25/automap-api-operation-handlers-in-components-based-project/index.html +++ b/articles/2020/10/25/automap-api-operation-handlers-in-components-based-project/index.html @@ -1,16 +1,10 @@ Automap API operation handlers in components-based project | aviskase -

    Automap API operation handlers in components-based project

    25 Oct 2020 -(upd: 28 Oct 2020)
    +(upd: 28 Oct 2020)
    2 mins

    Node.js best practices repo is the most comprehensive list of style guides and architectural tips for Node.js apps I’ve seen. The very first of them is about structuring projects based on components instead of layers.

    For example, the typical layers-based layout would be:

    .
     ├── common
     │   └── utils.ts
    @@ -48,6 +42,6 @@
             return handler[functionName];
         }
     }
    -

    The code was updated after the fix #426. Should be a valid example as of version v4.3.6.

    It assumes several things about the project’s structure:

    • All handlers are in the components/{componentName} directories.
    • All handlers have .router postfix in the filename
    • There could be several handlers inside component.
    • Default handler should have the same name as the component.
    • x-eov-operation-handler is equal to either component name (default handler) or {componentName}.{handler}.

    Why so many assumptions? First, I wanted it simple. Second, I avoided introducing libraries for glob file search (yes, Node.js doesn’t have globs in standard library, bleh). And third, it will be fairly easy to change anyway!

    The code was updated after the fix #426. Should be a valid example as of version v4.3.6.

    It assumes several things about the project’s structure:

    Why so many assumptions? First, I wanted it simple. Second, I avoided introducing libraries for glob file search (yes, Node.js doesn’t have globs in standard library, bleh). And third, it will be fairly easy to change anyway!

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/11/03/hmms-october/index.html b/articles/2020/11/03/hmms-october/index.html index 3d8ecef3..3b7f7c0f 100644 --- a/articles/2020/11/03/hmms-october/index.html +++ b/articles/2020/11/03/hmms-october/index.html @@ -1,13 +1,34 @@ Hmms: October | aviskase

    Hmms: October

    +3 Nov 2020
    2 mins

    Monthly collection of things that got me thinking hmming.

    No-code API platforms

    One of the recurring theme in the API domain is low– and no-code API solutions, so-called democratization of APIs. Common examples are IFTTT and Zapier.

    Yesterday I stumbled upon this video by Curtis McHale:

    It shows a very specific productivity workflow for iOS, but the curious thing is how much of it is automated by glueing together different apps via shortcuts. More eloquent writer could conjure the whole essay, but my thoughts so far are:

    • I, “lots-of-code” person, wouldn’t come up with such workflow. My routine automation tend to be one-to-one connections, whereas presented shortcut covers many apps.
    • UI-based automation mechanisms are limited but provide inspiration via affordances (see also).
    • Having a native OS mechanism enables the widest possible audience.
    • Services with apps shouldn’t limit integration capabilities only to web APIs; custom URL schemes are perfect for local app-to-app interactions.

    Hi, YouTube

    One day, as we were talking about our knowledge management systems in the Obsidian discord’s #russian channel, I decided to record a video to show mine.

    It’s in Russian and it’s bad xD

    Jeez, this was hard. Rerecording, cleaning up, cutting bits, and constantly, constantly hearing yourself in the process. And I didn’t even try to do anything fancy, just plain screen cast with editing out all the “eeeehm”. I’ll probably have to do the same in English, and the very thought terrifies me: it was painful enough to hear me speak in my native language. Guess how horrible will be that with the accent.

    older +MediaWiki’s discussion about moving to GitLab. Guess what, they used Fowler’s article as a basis for definitions in the discussion. It’s curious how some persons’ outputs instantly become almost a “standard.”

  • Uncle Bob explained SOLID yet again. What got my attention was how simple was his explanation.

  • No, I didn’t switch to JuliaMono font, but please check it out if you need a font with an enormous range of characters.

  • And take a look at draft for extending HTTP SEARCH method’s semantics. Perhaps, one day we will forget about GET and POST queries?

  • This tweet perfectly captures my feelings about new Google redesign.

  • No-code API platforms

    One of the recurring theme in the API domain is low– and no-code API solutions, so-called democratization of APIs. Common examples are IFTTT and Zapier.

    Yesterday I stumbled upon this video by Curtis McHale:

    It shows a very specific productivity workflow for iOS, but the curious thing is how much of it is automated by glueing together different apps via shortcuts. More eloquent writer could conjure the whole essay, but my thoughts so far are:

    • I, “lots-of-code” person, wouldn’t come up with such workflow. My routine automation tend to be one-to-one connections, whereas presented shortcut covers many apps.
    • UI-based automation mechanisms are limited but provide inspiration via affordances (see also).
    • Having a native OS mechanism enables the widest possible audience.
    • Services with apps shouldn’t limit integration capabilities only to web APIs; custom URL schemes are perfect for local app-to-app interactions.

    Hi, YouTube

    One day, as we were talking about our knowledge management systems in the Obsidian discord’s #russian channel, I decided to record a video to show mine.

    It’s in Russian and it’s bad xD

    Jeez, this was hard. Rerecording, cleaning up, cutting bits, and constantly, constantly hearing yourself in the process. And I didn’t even try to do anything fancy, just plain screen cast with editing out all the “eeeehm”. I’ll probably have to do the same in English, and the very thought terrifies me: it was painful enough to hear me speak in my native language. Guess how horrible will be that with the accent.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/11/30/hmms-november/index.html b/articles/2020/11/30/hmms-november/index.html index 5a9a1eba..588d1039 100644 --- a/articles/2020/11/30/hmms-november/index.html +++ b/articles/2020/11/30/hmms-november/index.html @@ -1,15 +1,24 @@ Hmms: November | aviskase

    Hmms: November

    +30 Nov 2020
    +3 mins

    Monthly collection of things that got me thinking hmming.

    Awards and reports

    Time for ThoughtWorks Technology Radar. One of the themes is democratizing programming which is tightly coupled with democratization of APIs I mentioned in the last hmms.

    The 2020 state of DevOps report is there! I haven’t even read it yet =)

    This year was the first time I attended DevPortal Awards. While there is a certain feeling of being too narrow (seriously, handful of portals getting several awards for several years?), it’s still useful for getting inspirations apart from usual “check Stripe docs!”

    In non-tech world, the Inglehart-Welzel World Cultural Map is all rad. There is no data point for Canada at the moment, but I’d guesstimate somewhere close to Great Britain and New Zealand. The most valuable thing is checking the dynamics between current and previous maps. For example, most of the red zone countries moved right, but the jump made by Belarus and Ukraine was bigger than by Russia; this seems to correlate with political and social events I know.

    Helpful guides

    Weird recommendation

    Watch this playlist (3 videos at the time of writing). You may think “wtf, Yuliya, this is some bullshit video game essays”. Yes, it is.

    But it is also one of the best videos explaining modern testing principles, agile, and lean. Damn, this could be on par with some TED talks. It also explains the appeal of APIs: modding in games is the similar concept to publishing APIs; you never know what users come up with.

    How about some quotes:

    Early access is your release.

    With rapid iterations DayZ mod evolved as the features were added based on what people wanted rather than what developer and publisher thought people wanted.

    […] design pressure from the bottom up.

    And, I don’t want to spoil it, but the juiciest part comes later with one of the best case studies for business owners: how to ignore the lucrative niche created for you by your players/users and lose the opportunity to grab the market. By lucrative I mean… even those who don’t play games know the biggest game in that genre ;)

    Floss dance

    Blog progress

    I have a bunch of articles in draft stage, but none got published this month, because I accidentally became a youtuber (Russian-only for now). Yay… Had to learn how to use all the tools, write scripts, and what not. I do love learning, but, jeez, this one takes time.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/12/07/crash-course-into-api-related-terminology/index.html b/articles/2020/12/07/crash-course-into-api-related-terminology/index.html index c383519a..39e5d72d 100644 --- a/articles/2020/12/07/crash-course-into-api-related-terminology/index.html +++ b/articles/2020/12/07/crash-course-into-api-related-terminology/index.html @@ -1,9 +1,9 @@ Crash course into API-related terminology | aviskase -

    Crash course into API-related terminology

    +7 Dec 2020
    5 mins

    Anyone trying to dive into the world of APIs is doomed to be confused by conflicting terminology. In this post I’m gonna touch upon the main definitions and processes. I already tried it once during lunch&learn session at work, but the time has come to revisit that old presentation in the written form.

    One last note: the basis for my definitions goes from OpenAPI Glossary started by Phil Sturgeon.

    Main definitions

    Specification is some standard or RFC or whatever describing a particular format or protocol.

    Schema (aka: data model) is a metadata describing the data type, formats, and validation rules. Schema can be defined for content body, header, path or query parameters, etc.

    Hypermedia controls is an easier way to say HATEOAS. What’s that? It’s a special affordance that allows a client to traverse API without hardcoding links. The simplest example would be including pagination links in the response data. E.g., a client asked to get collection of items — the server responded with first N items and a link to the next page of results — the client can use the link to get the next page without constructing a link on their own.

    {
         "items": [
             {"id": 1},
    @@ -13,13 +13,13 @@
             "next": { "href": "/api/items?cursor=ae12fb2" }
         }
     }
    -

    Description (aka: definition, contract) is a metadata about API, its endpoints, resources, operations, headers, parameters, and etc.

    API description document is a file that contains API description. It is usually written with a particular format from specifications like OpenAPI, WSDL, RAML.

    API documentation is a collection of information pieces that allows clients to successfully use an API. It includes, but not limited to, an API reference documentation (which is often generated from description document). Other important pieces are: tutorials, concept explanations, registration helpers, SDKs.

    Specifications (some of them)

    There are tons of API-related specifications. I’ll cover only some.

    OpenAPI is used to describe both the service model and the request/response body kinda based on JSON Schema. It was called Swagger before being donated to Linux Foundation. You probably noticed that this created a sore spot: people still call it Swagger, perhaps, because “OpenAPI” is a bit mouthful. Anyway, know that Swagger is just a part of the SwaggerHub branding of tooling like Swagger Editor or SwaggerUI.

    You may stumble upon people using the word “swagger” even in weirder situations:

    • as a synonym for “API”: “We developed swagger for creating users!”, “Use this swagger to export files!”
    • as a synonym for response body: “We make a request and the swagger we got back…”

    JSON Schema is for describing an instance of JSON data. OpenAPI was using an extended subset of it, but this was fixed in OAS 3.1, and now they are fully compatible. Why does it matter? Well, JSON Schema have additional tooling for client-side validations or HATEOAS support.

    JSON: API is an anti-bikeshedding spefication for building APIs. Imagine not inventing your own guidelines for naming and other patterns! A notable feature is ingrained support for HATEOAS.

    AsyncAPI is like OpenAPI but for event-driven architectures. Think Kafka, AMQP, WebSocket.

    Notable mentions: gRPC, GraphQL, OData, HAL, Siren.

    Processes

    API-first vs. API design-first vs. API code-first

    Read this article for more details. Here is a TL;DR.

    API-first is about how you approach your API as a product: not an afterthought on top of already created system, but thought through right from the start. The good example came recently from Obsidian’s devs:

    For the developer the biggest issue is migrating an existing app that wasn’t designed with plugin/API in mind. Having experienced this first hand with our previous product, we designed Obsidian from the ground up with a plugin API from the first version.

    API design-first means prototyping and testing a design before any coding is started. API code-first (or implementation-first) means having some code written even at the the design stage. Often though, a more restricted definition is used and it’s about the source of truth: description doc or code.

    • Design-first: “we write description doc first and then implement”.
    • Code-first: “we annotate code and generate a description doc from these annotations”.

    Beware, as I said, this is a more restricted definition! The article I’ve linked uses a broader one.

    Description doc validation vs. linting vs. preprocessing

    API description doc is often written in machine-readable format, thus, we can manipulate it and check for errors.

    Validation checks that description doc conforms to some specification.

    Linting checks custom rules like style. For example: check that all paths are lowercase.

    Preprocessing allows transforming the doc based on additional rules before using it in code or documentation generation. For example, hiding some endpoints from the published version or injecting descriptions from markdown files.

    $ref

    When working with OpenAPI or JSON Schema you will stumble upon $ref’s a great deal. These are pointers to JSON structures in different locations: inside the same file or to some other file:

    For example, this reference points to the schema from the other file.

    ...
    +

    Description (aka: definition, contract) is a metadata about API, its endpoints, resources, operations, headers, parameters, and etc.

    API description document is a file that contains API description. It is usually written with a particular format from specifications like OpenAPI, WSDL, RAML.

    API documentation is a collection of information pieces that allows clients to successfully use an API. It includes, but not limited to, an API reference documentation (which is often generated from description document). Other important pieces are: tutorials, concept explanations, registration helpers, SDKs.

    Specifications (some of them)

    There are tons of API-related specifications. I’ll cover only some.

    OpenAPI is used to describe both the service model and the request/response body kinda based on JSON Schema. It was called Swagger before being donated to Linux Foundation. You probably noticed that this created a sore spot: people still call it Swagger, perhaps, because “OpenAPI” is a bit mouthful. Anyway, know that Swagger is just a part of the SwaggerHub branding of tooling like Swagger Editor or SwaggerUI.

    You may stumble upon people using the word “swagger” even in weirder situations:

    • as a synonym for “API”: “We developed swagger for creating users!”, “Use this swagger to export files!”
    • as a synonym for response body: “We make a request and the swagger we got back…”

    JSON Schema is for describing an instance of JSON data. OpenAPI was using an extended subset of it, but this was fixed in OAS 3.1, and now they are fully compatible. Why does it matter? Well, JSON Schema have additional tooling for client-side validations or HATEOAS support.

    JSON: API is an anti-bikeshedding spefication for building APIs. Imagine not inventing your own guidelines for naming and other patterns! A notable feature is ingrained support for HATEOAS.

    AsyncAPI is like OpenAPI but for event-driven architectures. Think Kafka, AMQP, WebSocket.

    Notable mentions: gRPC, GraphQL, OData, HAL, Siren.

    Processes

    API-first vs. API design-first vs. API code-first

    Read this article for more details. Here is a TL;DR.

    API-first is about how you approach your API as a product: not an afterthought on top of already created system, but thought through right from the start. The good example came recently from Obsidian’s devs:

    For the developer the biggest issue is migrating an existing app that wasn’t designed with plugin/API in mind. Having experienced this first hand with our previous product, we designed Obsidian from the ground up with a plugin API from the first version.

    API design-first means prototyping and testing a design before any coding is started. API code-first (or implementation-first) means having some code written even at the the design stage. Often though, a more restricted definition is used and it’s about the source of truth: description doc or code.

    • Design-first: “we write description doc first and then implement”.
    • Code-first: “we annotate code and generate a description doc from these annotations”.

    Beware, as I said, this is a more restricted definition! The article I’ve linked uses a broader one.

    Description doc validation vs. linting vs. preprocessing

    API description doc is often written in machine-readable format, thus, we can manipulate it and check for errors.

    Validation checks that description doc conforms to some specification.

    Linting checks custom rules like style. For example: check that all paths are lowercase.

    Preprocessing allows transforming the doc based on additional rules before using it in code or documentation generation. For example, hiding some endpoints from the published version or injecting descriptions from markdown files.

    $ref

    When working with OpenAPI or JSON Schema you will stumble upon $ref’s a great deal. These are pointers to JSON structures in different locations: inside the same file or to some other file:

    For example, this reference points to the schema from the other file.

    ...
     application/json:
       schema:
         type: object
         properties:
           data:
             $ref: ../components/schemas/Users/User.yaml
    -

    References help with structuring schemas, separating them into reusable pieces.

    The process of looking for value of $ref is called resolution (or lookup). Not all tools and libraries play well with multi-file resolutions, so you may want to do one of these:

    • Bundling (aka: external inlining) replaces $ref’s to external files with internal references. This creates one file that is easier to share.
    • Dereferencing (aka: internal inlining) replaces $ref’s to external or internal data with actual data. The resulting file will have no $ref’s, but will be noticeably bigger.

    This post was triggered by discussion with Areti Panou in AB Testing podcast’s slack. I hope it will help you!


    See also:

    References help with structuring schemas, separating them into reusable pieces.

    The process of looking for value of $ref is called resolution (or lookup). Not all tools and libraries play well with multi-file resolutions, so you may want to do one of these:


    This post was triggered by discussion with Areti Panou in AB Testing podcast’s slack. I hope it will help you!


    See also:

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/12/20/using-openapi-cli-intro/index.html b/articles/2020/12/20/using-openapi-cli-intro/index.html index 1706d40b..8bbe32e3 100644 --- a/articles/2020/12/20/using-openapi-cli-intro/index.html +++ b/articles/2020/12/20/using-openapi-cli-intro/index.html @@ -1,18 +1,15 @@ Using openapi-cli: intro | aviskase

    Using openapi-cli: intro

    +20 Dec 2020
    +3 mins

    Before we jump into the usage of this mysterious tool with way too generic name, let me give you an introduction.

    There are many tools for supporting API design via OpenAPI description documents. You can check the list here.

    The most common tasks are:

    • dereferencing
    • linting
    • reference documentation preview and/or generation

    I talked about dereferencing not long ago. In short, it’s needed to produce a final OpenAPI description doc from more DRY and granular schemas. You can read more about the process here. One of the tools that can help you is json-schema-ref-parser.

    Linting is used to check OpenAPI description doc for errors and style guide violations. Spectral comes to mind; Arnaud Lauret has a great talk about using it for design reviews.

    SwaggerUI is the most common choice for reference documentation generation. If you need something more aesthetically pleasing you can try Redoc or RapiDoc.

    But there is a tool that combines all of these and more: welcome to openapi-cli (GitHub, docs). It supports both OpenAPI 2 (fka Swagger) and OpenAPI 3; support for 3.1 is coming soon. At the time of the writing, the tool’s version 1 is still in beta phase, so keep it in mind. But I’m fairly confident in using it anyway. Why? Two reasons.

    First of all, it’s developed by the same team as the famous Redoc, so you already know how reference docs look like =) Redoc is famous. You cannot read a book or attend a conference without seeing it mentioned by someone. A lot of people use it, so it would make sense for a openapi-cli to be developed further.

    The second reason is stated on the team’s site: “built because we needed it”. All tools developed by Redocly are used in the other company of its founder, Adam Altman, Rebilly. These tools weren’t created in vacuum; they are constantly dogfooded by the developers whose primary business is making good APIs. By the way, check their api definitions for an inspiration on how to structure description doc development.

    So, in this series of articles I’m gonna write about my experience of using openapi-cli.

    We gonna go through themes:

    And while I’m writing next posts (hehe), install and play with it!

    P.S. It’s a pity that Redocly team isn’t very active about advertising themselves. While everyone knows and praises Redoc, openapi-cli isn’t that well-known. Maybe the team wants to finish the beta first? Anyway, I was using it for almost a year, and I think it’s time to break the silence and give it some public love!

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2020/12/30/hmms-2020/aoc_hu9d52b6327b290172b4e8d6badca78555_124321_782x635_resize_q75_h2_box_3.webp b/articles/2020/12/30/hmms-2020/aoc_hu11813672139392634902.webp similarity index 100% rename from articles/2020/12/30/hmms-2020/aoc_hu9d52b6327b290172b4e8d6badca78555_124321_782x635_resize_q75_h2_box_3.webp rename to articles/2020/12/30/hmms-2020/aoc_hu11813672139392634902.webp diff --git a/articles/2020/12/30/hmms-2020/index.html b/articles/2020/12/30/hmms-2020/index.html index c9b76ae7..e43495a6 100644 --- a/articles/2020/12/30/hmms-2020/index.html +++ b/articles/2020/12/30/hmms-2020/index.html @@ -1,15 +1,51 @@ Hmms: 2020 | aviskase

    Hmms: 2020

    +30 Dec 2020
    +2 mins

    Usually it’s monthly collection of things that got me thinking hmming. But this is the end of the year, let’s do a little retrospective.

    Retro

    What happened this year:

    • My title switched from “tester” to “engineer”. Yay?
    • Had a great time at ASC 2020 conference. Yes, it’s not my first conference, but this one left a good long-lasting aftertaste.
    • I ported blog from pelican to hugo and started a redesign. Still much to do and polish, but at least the base is sound.
    • Joined Obsidian community.
    • As a consequence, I unwillingly created a YouTube channel in Russian. It’s cool to learn new skills (audio cleaning, video editing), but to be honest, content creation in Russian isn’t exactly a lucrative or professionally valuable hobby. Still, I cannot say no to people asking to teach or explain something: knowledge sharing is one of the biggest values to me.
    • Finished Finnish tree on Duolingo. Don’t ask why, I, person living in Quebec, learn Finnish. I just want to! Se on täydellinen kieli.

    What didn’t happen:

    • Bought a graphic tablet but didn’t do any drawing lessons. Though, I do use it for YouTube.
    • The guitar is collecting dust again.
    • Didn’t go to Kazakhstan.

    2020 taught many not to make plans, good thing I never was much of the “goals” person anyway. Still, here are my themes for 2021:

    • At least once be a conference speaker or a podcast guest.
    • Content creation: stable every other week, either this blog or Russian YouTube channel (or maybe English?). Concentrate more on tutorials.
    • Guitar. Must. Be. Conquered. To make the promise more realistic, I hereby vow to start daily lessons as soon as I finish levelling my main in ESO to CP 810 (yup, I’m still playing it).
    • Begin drawing lessons in the 2nd half of the year. Luckily, there was a great post on Habr about it.

    You may notice almost an absence of professional goals. Typical end-of-year posts often contain promises to read more or learn new professional skills/tools. But my to-read/to-watch/to-learn queue is on the opposite side: it must be scaled down occasionally; I’m definitely not one of those people who seems to have an endless fountain of energy to do everything (aksi, talking about ya here).

    Advent of code

    This was the first time I did Advent of code with the devs from work: nice to compare with normal people and not reddit stars. Cannot express how happy I’m to be back to Python, it’s so readable and clear after JS/TS madness.

    Advent of Code easter egg: using itertools

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2021/01/10/using-openapi-cli-for-api-exploration/index.html b/articles/2021/01/10/using-openapi-cli-for-api-exploration/index.html index bf7d177c..3012d388 100644 --- a/articles/2021/01/10/using-openapi-cli-for-api-exploration/index.html +++ b/articles/2021/01/10/using-openapi-cli-for-api-exploration/index.html @@ -1,13 +1,40 @@ Using openapi-cli for API exploration | aviskase

    Using openapi-cli for API exploration

    +10 Jan 2021
    +4 mins

    As promised, let’s dive into the usage of openapi-cli. The first topic is semi-non-technical: API exploration. You might be interested if:

    • you’re a tech writer
    • you’re a tester
    • you don’t know what the heck API exploration is

    What is API exploration

    I use this term akin to exploratory testing. You can search for works of Cem Kaner, James Bach, Michael Bolton, Elisabeth Hendrickson, James Whittaker, but beware, that’s a nice little serpentarium of a field.

    In the simplest words, it is a process of learning something about API I don’t know or have a limited information about. That’s it. The main contexts for this activity are:

    • Necessity. I want to use some API.
    • Curiosity. Just looking at how other APIs are done.
    • Assessment. I need to check APIs for certain characteristics.

    There are many ways to perform exploration, countless tools and sources to use. In this article I’ll be looking only at OpenAPI description documents, because that’s where openapi-cli comes handy.

    When exploring unknown APIs, I run a globally installed package: npm i -g @redocly/openapi-cli. Then it is accessible in the terminal as openapi <args>.

    openapi stats

    Gathers some basic statistics for an OpenAPI description document. Can be in “stylish” format (as in the example below) or in JSON.

    ➜ openapi stats http://localhost:8888/api/v1/swagger.json
     Document: http://localhost:8888/api/v1/swagger.json stats:
     
     🚗 References: 12 
    @@ -31,7 +58,7 @@
     Bundling...
     
     Created a bundle for http://localhost/api/v1/swagger.json successfully
    -

    Why you’d want that?

    First, sometimes there is just an OpenAPI description document and nothing else. No documentation whatsoever. Rare, but I do stumble upon this with internal APIs.

    The second reason is when there are some reference docs, but they don’t fit you.

    For example, it’s a SwaggerUI, and you don’t like it, or the description doc has advanced features like oneOf or discriminator. In such case, SwaggerUI cannot generate fancy form-based presentation, so you have to read bare model definitions.

    Or perhaps you have special needs. Lorna Jane Mitchell in her presentation on Delightful SDKs mentions that she often generates her own local reference docs because official documentations aren’t accessible. AFAIK, she uses Redoc and made several pull requests specifically for better accessibility support.

    You can generate reference docs with redoc-cli, but I prefer using openapi-cli as a one-stop shop. The difference is when you want to generate static HTML with your theme: right now you have to use a small hack from this issue.

    openapi split

    Reading a big OpenAPI description doc is cumbersome. Especially when you need to jump back and forth between references.

    Split for the rescue! It’s an opposite of the bundling operation and separates references into several files. Though, it’s a bit smarter; for example, it splits paths into separate files too.

    This command doesn’t support OpenAPI 2.

    Let’s split a description doc into out directory: openapi split --outDir out openapi.json. Here is a sample result:

    ~/out 
    +

    Why you’d want that?

    First, sometimes there is just an OpenAPI description document and nothing else. No documentation whatsoever. Rare, but I do stumble upon this with internal APIs.

    The second reason is when there are some reference docs, but they don’t fit you.

    For example, it’s a SwaggerUI, and you don’t like it, or the description doc has advanced features like oneOf or discriminator. In such case, SwaggerUI cannot generate fancy form-based presentation, so you have to read bare model definitions.

    Or perhaps you have special needs. Lorna Jane Mitchell in her presentation on Delightful SDKs mentions that she often generates her own local reference docs because official documentations aren’t accessible. AFAIK, she uses Redoc and made several pull requests specifically for better accessibility support.

    You can generate reference docs with redoc-cli, but I prefer using openapi-cli as a one-stop shop. The difference is when you want to generate static HTML with your theme: right now you have to use a small hack from this issue.

    openapi split

    Reading a big OpenAPI description doc is cumbersome. Especially when you need to jump back and forth between references.

    Split for the rescue! It’s an opposite of the bundling operation and separates references into several files. Though, it’s a bit smarter; for example, it splits paths into separate files too.

    This command doesn’t support OpenAPI 2.

    Let’s split a description doc into out directory: openapi split --outDir out openapi.json. Here is a sample result:

    ~/out 
     ➜ tree
     .
     ├── components
    @@ -55,6 +82,6 @@
     └── paths
         ├── users@{id}.yaml
         └── users.yaml
    -

    Another reason to use split is when you’re not just exploring API, but you plan to work on it. For example, when you’re switching from annotations-driven to handcrafted approach; split creates a good starting point for refactoring.

    openapi lint

    You may want to do linting in assessment or curiosity contexts:

    • Assessment:
      • Is this old API at least valid per OpenAPI standard or it’s complete garbage?
      • We came up with new API guidelines. Do our existing APIs conform to it? How many don’t?
    • Curiosity:
      • I wonder how many APIs out there follow this rule? Is it common and should we apply it in our API?

    As you can see, the biggest benefits of openapi lint come when you use custom rules and configurations. This topic I’ll be covering in later articles, stay tuned.


    If you want to read more about OpenAPI-based API exploration, I recommend Arnaud Lauret’s series of articles about jq.


    See also:

    Another reason to use split is when you’re not just exploring API, but you plan to work on it. For example, when you’re switching from annotations-driven to handcrafted approach; split creates a good starting point for refactoring.

    openapi lint

    You may want to do linting in assessment or curiosity contexts:

    As you can see, the biggest benefits of openapi lint come when you use custom rules and configurations. This topic I’ll be covering in later articles, stay tuned.


    If you want to read more about OpenAPI-based API exploration, I recommend Arnaud Lauret’s series of articles about jq.


    See also:

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/index.html b/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/index.html index 02aea05a..5dfeaeff 100644 --- a/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/index.html +++ b/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/index.html @@ -1,13 +1,25 @@ Using openapi-cli during API design: part one | aviskase

    Using openapi-cli during API design: part one

    +31 Jan 2021
    +7 mins

    Obviously, API design is much more than writing OpenAPI description doc. First of all, should it even be OpenAPI-based? If yes, using openapi-cli will make your life a bit easier.

    Example API

    Let’s imagine you’ve been asked to create an API for Stargate Network and you already did some preliminary analysis.

    Stargate network consists of points in space identifiable via special addresses. When you dial an address (like a phone number), a wormhole is established between your gate and a gate at the address. Gates are physical objects, so they can be destroyed or moved to another address.

    First, resources:

    • Address
      • Id
      • Is the address accessible? Can we dial it?
      • last known position in human terms (some addresses aren’t fixed points in space)
      • Symbols used to dial it
        • in galaxy and out of galaxy addresses
        • unique address if it assigned to the gate
      • Available gates at this address
    • Stargate
      • Id
      • Environment: is it located above the surface, underwater, on the surface, or inside the ship?
      • State: is the gate functional, destroyed, or buried?
      • Address id

    Next, operations. Nothing fancy, the bare minimum:

    • List all addresses
    • Get address info
    • Add new gate info
    • Get gate info
    • Update gate info

    Don’t forget, not all APIs should be OpenAPI-based. If I needed to design a real Stargate system, I would go with a combo of something like GraphQL for lookup and event-based APIs for anything else (perhaps described with AsyncAPI).

    Let’s create a place to store our OpenAPI description doc: YAML-based, multi-file git repo. You can read more about why’s in the Redocly docs.

    The example repo is on the GitHub. At each step of the tutorial you’ll see links to commits as Commit <commit_hash>.

    Repository structure

    We’re gonna use create-openapi-repo to generate the initial structure and then tweak it a bit. Run npx create-openapi-repo inside a directory where you want to generate API structure. One caveat to keep in mind: this tool will attempt to initialize a git repository and make a commit. Commit e5b40a5.

    Output of create-openapi-repo tool

    Let’s explore the generated files:

    • docs directory contains HTML template for reference doc. I’m deleting it, because I don’t need any custom template at the moment.
    • .gitignore set up to ignore our beloved node_modules and dist directory used for generating the final OpenAPI description doc.
    • .redocly.yaml is a configuration file for openapi-cli. Because I removed docs directory, I also need to remove referenceDocs.htmlTemplate option here.
    • Don’t forget to update the LICENSE.
    • package.json set up with the most common actions.
    • Take your time to read README.md. It explains what actions are available and provides a sample contribution guidelines. In the real project, you probably don’t want to have all guidelines there in one file.
    • And last, but not least, openapi directory. This is where your API definitions live. Almost all subdirectories have a relevant README.md explaining why these particular files were created as well as showing you other alternatives. I don’t want to copy-paste them here, so, go ahead and explore ;)

    Now, let’s rewind the time. Stargate Network API contract is defined, commit c2b8d11. For more information about keeping your structure DRY read this and this. If you’re interested in reading/watching about it from me, just ping!

    The definitions are not state of art, even for this shallow design. Some problems with it were added consciously to simplify the example, others for fixing in later articles.

    And I’m not even talking about how many details are omitted lore-wise!

    Preview

    In the package.json file there is already an action for previewing the docs: openapi preview-docs. To execute it, run in the terminal npm run start.

    This starts a server with the generated Redoc reference documentation (docs). It watches changes from the disk; usually I keep it running while writing definitions and use it like a UI of Stoplight Studio or Insomnia Designer.

    As you may (or may not) know, you can customize how Redoc looks like. This is done withreferenceDocs section in .redocly.yaml (docs). Beware, some options are available only for premium edition.

    Options can be divided into two categories: those that affect a theme (colors, fonts) and those for disabling or enabling certain features. For example, requiredPropsFirst: true will display required properties before others:

    Comparison of property display with requiredPropsFirst equal to false or true

    See at commit c873f15.

    Linting

    From time to time you should lint with openapi lint (docs). Let’s execute npm run test.

    ➜ npm run test
     
     > stargate-network@1.0.0 test /home/aviskase/Projects/openapi-cli-examples
     > openapi lint
    @@ -44,6 +56,6 @@
     📦 Created a bundle for /home/aviskase/Projects/openapi-cli-examples/openapi/openapi.yaml at dist.yaml 28ms.
     

    Hmm, what just happened? Instead of ./dist/<filename>.yaml we’ve got ./dist.yaml. Why?

    Well, this is kinda bug or feature situation. If you check the code, this looks like an intended behavior, but nevertheless confusing. In short, if you have only one API definition, -o seems to be expecting file path. If you have multiple API definitions (more about that in the upcoming part two), this option can point to directory.

    In our example we have one API definition, so to fix we should change the script either to -o dist/openapi or to -o dist/openapi.yaml. Both will work the same, as long as --ext equals to yaml by default. If you want to generate file in JSON format, you can use -o dist/openapi --ext json or -o dist/openapi.json. If you want both formats, you need to run commands separately, see at commit a4e1e23.

    Generating reference doc

    One of the features that openapi-cli doesn’t have, but I need all the time is to generate static Redoc html file. Yes, you’d want to invest in better solutions and processes at some point, but at smaller scale it’s ok to send a simple HTML file when someone from non-dev team wants to see the latest or prototype API thingy.

    First, we need to install redoc-cli via npm i redoc-cli. Then, add a new script to package.json to generate an HTML file inside dist directory:

    "docs": "redoc-cli bundle dist/openapi.yaml -o dist/redoc.html"
     

    After running npm run docs and opening the generated file you’ll notice that all our custom options are gone. You see, redoc-cli doesn’t support .redocly.yaml configuration file. To fix this, add prepareOptions.js which reads referenceDocs from .redocly.yaml and saves it to .redoc.json Next, modify docs script to run preparation script and use .redoc.json:

    "docs": "node plugins/prepareOptions.js && redoc-cli bundle dist/openapi.yaml -o dist/redoc.html --options .redoc.json"
    -

    See at commit ad6dacc.

    IMHO

    Personally, I prefer different names for scripts:

    • preview instead of start
    • lint instead of test
    • bundle instead of build
    • build for npm run lint && npm run bundle && npm run docs

    Then, all I do is jump between npm run preview and npm run build.

    See at commit 51c078e.


    That’s it for today. In the part two, I’ll show how to manage multiple definitions per repository.

    See at commit ad6dacc.

    IMHO

    Personally, I prefer different names for scripts:

    Then, all I do is jump between npm run preview and npm run build.

    See at commit 51c078e.


    That’s it for today. In the part two, I’ll show how to manage multiple definitions per repository.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/initRepo_hu7d57596270b7696f2b8b84448070eccd_129657_1517x582_resize_q75_h2_box_3.webp b/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/initRepo_hu12054594018965230055.webp similarity index 100% rename from articles/2021/01/31/using-openapi-cli-during-api-design-part-one/initRepo_hu7d57596270b7696f2b8b84448070eccd_129657_1517x582_resize_q75_h2_box_3.webp rename to articles/2021/01/31/using-openapi-cli-during-api-design-part-one/initRepo_hu12054594018965230055.webp diff --git a/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/requiredProps_hu69bdac1b5e8f9744325b681df1e2e84d_152272_2080x729_resize_q75_h2_box_3.webp b/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/requiredProps_hu9150255185689010804.webp similarity index 100% rename from articles/2021/01/31/using-openapi-cli-during-api-design-part-one/requiredProps_hu69bdac1b5e8f9744325b681df1e2e84d_152272_2080x729_resize_q75_h2_box_3.webp rename to articles/2021/01/31/using-openapi-cli-during-api-design-part-one/requiredProps_hu9150255185689010804.webp diff --git a/articles/2021/03/23/using-openapi-cli-during-api-design-part-two/index.html b/articles/2021/03/23/using-openapi-cli-during-api-design-part-two/index.html index bd0df73f..aadeac75 100644 --- a/articles/2021/03/23/using-openapi-cli-during-api-design-part-two/index.html +++ b/articles/2021/03/23/using-openapi-cli-during-api-design-part-two/index.html @@ -1,12 +1,24 @@ Using openapi-cli during API design: part two | aviskase

    Using openapi-cli during API design: part two

    +23 Mar 2021
    2 mins

    Part one showed the basics of using openapi-cli for a single-definition project. Let’s see how it works for multi-definition projects.

    Multi-what?

    By multi-definition project I mean a project with several OpenAPI description documents. For example, it can be useful if you want to keep contracts for all services in one place. Then, you can reuse common schemas between definitions.

    Another use case is keeping parts of your APIs hidden from the final description document (internal and external APIs). This approach will mostly work only if separation is on path-level (see more here).

    Configuration

    In our small Stargate API example we are splitting gates and addresses APIs into two description docs (commit). Next step is to change configuration file, so that openapi-cli would know that we have two definitions (commit).

    apiDefinitions:
       gates: openapi/gates.yaml
       addresses: openapi/addresses.yaml
    @@ -23,6 +35,6 @@
     	"docs": "npm run docs:gates && npm run docs:addresses",
     	...
     }
    -

    There is no need to run prepareOptions.js twice, but I’m keeping it in case I’d want to generate only one reference doc.


    The final working package.json is here. As you can see, we have to manually manage some building processes, but the most important pieces (linting & bundling) are quite transparent.

    In the next article we’re gonna finally see juicy linting magic!

    There is no need to run prepareOptions.js twice, but I’m keeping it in case I’d want to generate only one reference doc.


    The final working package.json is here. As you can see, we have to manually manage some building processes, but the most important pieces (linting & bundling) are quite transparent.

    In the next article we’re gonna finally see juicy linting magic!

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2021/08/16/using-openapi-cli-custom-preprocessing/index.html b/articles/2021/08/16/using-openapi-cli-custom-preprocessing/index.html index d5b498d5..d74d3b62 100644 --- a/articles/2021/08/16/using-openapi-cli-custom-preprocessing/index.html +++ b/articles/2021/08/16/using-openapi-cli-custom-preprocessing/index.html @@ -2,11 +2,11 @@

    Using openapi-cli: custom preprocessing

    +16 Aug 2021
    4 mins

    The key feature of openapi-cli is its extensibility. There are three ways to extend it: preprocessors, rules, and decorators. In comparison, Spectral supports only custom rules.

    In this tutorial, we’ll start with preprocessors. They are used to transform OpenAPI description document before validation and linting. Mind you, documentation says they should be avoided, because custom preprocessors tend to be error prone.

    Example: extensible enumerations

    In our Stargate Network API there is a Stargate.yaml schema with two properties defined with enumerations: state and environment. We might think that we captured all potential values, but we can’t be sure.

    Zalando API guidelines and The Design of Web APIs by Arnaud Lauret warn us that enumerations can cause API compatibility breaks, especially with outputs.

    Imagine the scenario. We generated SDK for the initial version of the API, and the generator was smart enough to use enumerations in the selected programming language. Some time later we push a new API version where environment can also be STAR. Customers, who are still on the first version of the SDK, won’t be able to process API responses containing this value, because most probably their clients will crash with something-something-uprocessable-entity exceptions.

    Thus, similarly to Zalando, we will use a custom vendor extension to document possible values. I’m calling it x-aviskase-enum just in case not to introduce conflicts with other extensions. Commit fa78766.

    Surfacing values in the documentation

    Oops, now there is no way to see known values in the generated reference docs. The manual fix is to add these values to the description. But this is too cumbersome, especially if you’ll ever want to change presentation format.

    Preprocessor for the rescue! What we want it to do:

    1. Find all properties with x-aviskase-enum property.
    2. Grab the list of values, format them nicely, and add it to the description. Make sure not to overwrite existing description if it’s already present!

    Adding a preprocessor

    Let’s create a preprocessor first ./plugins/preprocessors/add-enum-to-description.js:

    //@ts-check
     module.exports = AddEnumToDescription;
     
    @@ -29,7 +29,7 @@
       }
       return additionalDescription;
     }
    -

    What’s happening here:

    • We export a preprocessor function AddEnumToDescription that accepts configuration options:
      • vendorExtension option is used to define extension name for extensible enumeration. If not provided, it equals to x-enum.
    • All openapi-cli preprocessors should return a visitor. In our case, we specifically indicate to take into account only schema nodes.
    • If a schema has defined extensible enumeration property, we update its description with formatted list of values.

    Per documentation, for type support you can use import('@redocly/openapi-cli').Oas3Preprocessor}. It never worked for me, so I use a “full path” import('@redocly/openapi-core/src/visitors').Oas3Preprocessor.

    Enabling our preprocessor via custom plugin

    Let’s keep our preprocessor (and the future ones) bundled inside one plugin via ./plugins/custom-preprocessors.js:

    //@ts-check
    +

    What’s happening here:

    • We export a preprocessor function AddEnumToDescription that accepts configuration options:
      • vendorExtension option is used to define extension name for extensible enumeration. If not provided, it equals to x-enum.
    • All openapi-cli preprocessors should return a visitor. In our case, we specifically indicate to take into account only schema nodes.
    • If a schema has defined extensible enumeration property, we update its description with formatted list of values.

    Per documentation, for type support you can use import('@redocly/openapi-cli').Oas3Preprocessor}. It never worked for me, so I use a “full path” import('@redocly/openapi-core/src/visitors').Oas3Preprocessor.

    Enabling our preprocessor via custom plugin

    Let’s keep our preprocessor (and the future ones) bundled inside one plugin via ./plugins/custom-preprocessors.js:

    //@ts-check
     const AddEnumToDescription = require('./preprocessors/add-enum-to-description');
     const id = 'custom-preprocessors';
     
    @@ -52,6 +52,6 @@
         custom-preprocessors/add-x-enum-to-description:
           vendorExtension: x-aviskase-enum
     ...
    -

    The final result is in commit 6654926.

    How it looks in the docs

    Now, when we render a reference documentation, we can see all values with simple markdown formatting.

    Redoc with modified descriptions for extensible enumerations

    By the way, can you spot operations where we could have left normal enum and why?

    Why use a preprocessor here?

    As mentioned before, preprocessors might be brittle. We could have used decorator instead. I’ll show you the reason in the next article, but here is a sneak peek: we want to be able to lint modified descriptions!

    The final result is in commit 6654926.

    How it looks in the docs

    Now, when we render a reference documentation, we can see all values with simple markdown formatting.

    Redoc with modified descriptions for extensible enumerations

    By the way, can you spot operations where we could have left normal enum and why?

    Why use a preprocessor here?

    As mentioned before, preprocessors might be brittle. We could have used decorator instead. I’ll show you the reason in the next article, but here is a sneak peek: we want to be able to lint modified descriptions!

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2021/08/16/using-openapi-cli-custom-preprocessing/x_enum_render_hu0c23a21c68146543c289aaa72aeaae18_43182_1445x324_resize_q75_h2_box_3.webp b/articles/2021/08/16/using-openapi-cli-custom-preprocessing/x_enum_render_hu9370951611690197926.webp similarity index 100% rename from articles/2021/08/16/using-openapi-cli-custom-preprocessing/x_enum_render_hu0c23a21c68146543c289aaa72aeaae18_43182_1445x324_resize_q75_h2_box_3.webp rename to articles/2021/08/16/using-openapi-cli-custom-preprocessing/x_enum_render_hu9370951611690197926.webp diff --git a/articles/2021/09/06/using-openapi-cli-custom-rules/index.html b/articles/2021/09/06/using-openapi-cli-custom-rules/index.html index 0fd43a64..8407891a 100644 --- a/articles/2021/09/06/using-openapi-cli-custom-rules/index.html +++ b/articles/2021/09/06/using-openapi-cli-custom-rules/index.html @@ -1,12 +1,9 @@ Using openapi-cli: custom rules | aviskase -

    Using openapi-cli: custom rules

    +6 Sep 2021
    4 mins

    If you’re planning to lint OpenAPI description documents (you should!), always check whether a linter supports adding custom rules. And I mean not just changing severity or disabling predefined rules, but actually adding new ones specific to your API standards.

    openapi-cli, as any respectable OpenAPI linter, allows that. The process is very similar to adding preprocessors.

    Example: enforcing case style

    Let’s look at our previous enumeration example:

    state:
     	type: string
     	nullable: true
    @@ -145,6 +142,6 @@
     20 |   x-aviskase-enum:
     
     Error was generated by the custom-rules/description-style rule.
    -

    Hmm, looks confusing. Thanks to suggest we can at least see that something was added to the original description… Oh, right, that was our preprocessor! That’s why I suggested to use it in the first place: if we were to modify descriptions via decorators, the rule wouldn’t be able to catch this problem.

    Finally, to make linter passing, we need to fix the preprocessor plugin code commit 81a4332.

    Rules are cooler than preprocessors and decorators: they support nested visitors. I’ll cover this concept in the next article. Until then, try to come up with an example where the uppercase rule in its current form might have undesired behavior.

    Hmm, looks confusing. Thanks to suggest we can at least see that something was added to the original description… Oh, right, that was our preprocessor! That’s why I suggested to use it in the first place: if we were to modify descriptions via decorators, the rule wouldn’t be able to catch this problem.

    Finally, to make linter passing, we need to fix the preprocessor plugin code commit 81a4332.

    Rules are cooler than preprocessors and decorators: they support nested visitors. I’ll cover this concept in the next article. Until then, try to come up with an example where the uppercase rule in its current form might have undesired behavior.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2023/12/15/the-navys-it-challenges/index.html b/articles/2023/12/15/the-navys-it-challenges/index.html index 831144bf..8ab9978b 100644 --- a/articles/2023/12/15/the-navys-it-challenges/index.html +++ b/articles/2023/12/15/the-navys-it-challenges/index.html @@ -1,15 +1,15 @@ The Navy’s IT challenges | aviskase

    The Navy’s IT challenges

    +15 Dec 2023
    +2 mins

    Can’t stop thinking about one line from the recent Paul Well’s interview with Vice Admiral Angus Topshee, the Commander of the Royal Canadian Navy.

    PW: Is the fact that this video’s on YouTube a reflection of any difficulty you’re having getting heard internally?

    AT: No. I’ve shared my assessments with the leadership of the Department, up to and including this minister and the previous Minister. I feel I’m being heard and respected. This was more about, we wanted to put the message out internally. Unfortunately, there’s some very specific IT challenges around how we disseminate internal messages. And so [Youtube] was the channel we used.

    Specific IT challenges, eh?

    I guess most of the people skim through this line. Whatever, it’s just a pretext. But for me it sounds bizarrely specific. So, here some of my musings about it.

    Those problems with internal communications. It got me curious, how really it’s even done in the military. “Army-wide” emails? Slack, Zoom, Teams? IRC? The answer is all of these. Total chaos. I especially like the comment mentioning using Facebook Messenger, because, c’mon! At least, no WeChat. I hope. I also hope they have less problems accessing their Top Secret Network stuff, not like our ministers [page 27].

    While we already established that, in fact, it’s way easier to just publicly share YouTube video than send a file or a private link, I highly doubt it was the main reason. Thus, I wonder, why IT was selected as a pretext. Why. Is it something on admiral’s mind? Or is it a common “enemy” that always gets blamed and everyone nods understandingly? Is IT a dog that ate the homework in government offices? 🙄


    Yeah, I’m back. Kinda. No promises.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2024/01/25/open-banking-canada-vs-kazakhstan/index.html b/articles/2024/01/25/open-banking-canada-vs-kazakhstan/index.html index 93c0fd03..96d13183 100644 --- a/articles/2024/01/25/open-banking-canada-vs-kazakhstan/index.html +++ b/articles/2024/01/25/open-banking-canada-vs-kazakhstan/index.html @@ -1,12 +1,15 @@ Open Banking: Canada vs Kazakhstan | aviskase

    Open Banking: Canada vs Kazakhstan

    +25 Jan 2024
    +2 mins

    As I was skimming through The API Changelog issue 2024.04, my eye caught:

    Kazakhstan’s National Bank and other entities published a report on an Open API and Open Banking pilot, aiming to modernize financial services through standardized data exchange. The successful pilot involved multiple banks and clients, setting the stage for a transparent and competitive financial sector by 2024.

    Cool. Let’s read the report.

    The pilot project was carried out with the participation of focus groups of 128 clients and second-tier banks, including Bank RBK, Altyn Bank, Home Credit Bank, Bank CenterCredit, and Otbasy Bank.

    The country now follows the concept of development of Open API and Open Banking for 2023-2025, which was approved by the National Bank of Kazakhstan, the Agency for Regulation and Development of Financial Market, and the Agency for Protection and Development of Competition in December 2022.

    Yup, Halyk Bank isn’t mentioned, so it’s just a really early pilot. Even more, no Kaspi Bank. It’s like doing something in Canada without involving Big Five. Well, they have 2 more years, so, good luck.

    Hmm. Speaking of. What about Canada?

    Despite Canada’s reputation for bad information disclosure approaches, I do enjoy the content available on canada.ca. Including what’s up with open banking. It’s a long page, but all you need is the first sentence:

    Open banking, also called consumer-driven banking, is not yet available in Canada.

    Ouch. Is there any progress, though? According to 2023 Fall Economic Statement: Policy Statement on Consumer-Driven Banking:

    The Department of Finance will advance the work required to stand up a Canadian framework governing consumer-driven banking, with the goal of adopting legislation and fully implementing the necessary governance framework by 2025.

    Same year, different goals. Kazakhstan is planning to finish implementing (doubt it), whereas Canada is planning to finish with laws and paperwork (doubt it too). Let’s put a reminder and check on them both in 2025?

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2024/02/23/two-ten-years-of-february/index.html b/articles/2024/02/23/two-ten-years-of-february/index.html index ffddd0c4..fd03d387 100644 --- a/articles/2024/02/23/two-ten-years-of-february/index.html +++ b/articles/2024/02/23/two-ten-years-of-february/index.html @@ -1,16 +1,43 @@ Two (ten) years of February | aviskase

    Two (ten) years of February

    +23 Feb 2024
    1 min

    Today I’d like to share this bit about my great-grand father Bagriy Ivan:

    The last bunker in our region was destroyed by the NKVD in 1952 in the Lubianets forest. -Four shylyans were killed then:

    • Antoniuk Volodymyr - born in 1923.
    • Bagriy Ivan - born in 1911.
    • Voznyi Ivan - born in 1905.
    • Hulovskyi Vasyl - born in 1918.

    Nineteen fifty-two. By that time, Ivan’s wife and son (my grandfather) had already been exiled to Kazakhstan.

    One can write a lengthy eloquent essay about never surrendering, believing in independence, and so on. I won’t.

    Слава Україні. Героям слава.

    older +Four shylyans were killed then:

    • Antoniuk Volodymyr - born in 1923.
    • Bagriy Ivan - born in 1911.
    • Voznyi Ivan - born in 1905.
    • Hulovskyi Vasyl - born in 1918.

    Nineteen fifty-two. By that time, Ivan’s wife and son (my grandfather) had already been exiled to Kazakhstan.

    One can write a lengthy eloquent essay about never surrendering, believing in independence, and so on. I won’t.

    Слава Україні. Героям слава.

    older   ·   ·   ·   -newer
    \ No newline at end of file +newer \ No newline at end of file diff --git a/articles/2024/03/16/goodbye-ogimage/index.html b/articles/2024/03/16/goodbye-ogimage/index.html index e405882a..8043db06 100644 --- a/articles/2024/03/16/goodbye-ogimage/index.html +++ b/articles/2024/03/16/goodbye-ogimage/index.html @@ -1,12 +1,42 @@ Goodbye, og:image | aviskase

    Goodbye, og:image

    +16 Mar 2024
    2 mins

    When you see a link on almost all social platforms or messengers, you usually see a card preview with a title, an almost invisible summary, and an image. These cards are generated based on twitter and Open Graph HTML tags that look something like this:

    <meta property="og:url" content="<url>">
     <meta property="og:title" content="<title>">
     <meta property="og:description" content="<description>">
    @@ -17,4 +47,4 @@
     <meta name="twitter:title" content="<title>">
     <meta name="twitter:description" content="<description>">
     <meta name="twitter:image" content="<link>">
    -

    See those image properties? Per specifications they are required. The minimum size is 200×200 pixels; the recommended size is 1200×630 pixels.

    The goal is to have a nice link preview. Fine. If the article already has a relevant image, why not? The problem is, not all articles have them, so people improvise:

    • Find some semi-relevant stock image.
    • Ask somebody to create one. That’s for fancy websites.
    • Generate image using Midjourney or whatever “AI” you prefer.
    • Generate image using article title (I used to do that).

    First three are eye candy solutions. Last one is basically duplicating already displayed information.

    So, we are trying to conform to some arbitrary standards imposed by social media platforms. These images are there to drive engagement. To be attention grabs. And no one likes to read nowadays, right? Thus, one could argue that continuing this practice is in the best interest of content creators. However, if this is the goal…

    Boobs. Just make an image with the boobs. You can even add some article-relevant background. If you’re creating the image solely because “Facebook says so”, why limit yourself to half-measures? Drive your SEO by ░C░O░N░T░E░N░T░ ░I░N░ ░B░I░O░.


    Anyway, yeah, no Open Graph images here anymore.

    \ No newline at end of file +

    See those image properties? Per specifications they are required. The minimum size is 200×200 pixels; the recommended size is 1200×630 pixels.

    The goal is to have a nice link preview. Fine. If the article already has a relevant image, why not? The problem is, not all articles have them, so people improvise:

    First three are eye candy solutions. Last one is basically duplicating already displayed information.

    So, we are trying to conform to some arbitrary standards imposed by social media platforms. These images are there to drive engagement. To be attention grabs. And no one likes to read nowadays, right? Thus, one could argue that continuing this practice is in the best interest of content creators. However, if this is the goal…

    Boobs. Just make an image with the boobs. You can even add some article-relevant background. If you’re creating the image solely because “Facebook says so”, why limit yourself to half-measures? Drive your SEO by ░C░O░N░T░E░N░T░ ░I░N░ ░B░I░O░.


    Anyway, yeah, no Open Graph images here anymore.

    older
    \ No newline at end of file diff --git a/css/main.min.896ab26ea71b26f2c4f733d8e195dba6b3ba658b87acfad1f9fd04db745860aa.css b/css/main.min.896ab26ea71b26f2c4f733d8e195dba6b3ba658b87acfad1f9fd04db745860aa.css deleted file mode 100644 index 67e2a5bc..00000000 --- a/css/main.min.896ab26ea71b26f2c4f733d8e195dba6b3ba658b87acfad1f9fd04db745860aa.css +++ /dev/null @@ -1 +0,0 @@ -*,::before,::after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}::before,::after{--tw-content:''}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Nunito,Univers,Calibri,gill sans,gill sans mt,myriad pro,Myriad,dejavu sans condensed,liberation sans,nimbus sans l,Tahoma,Geneva,helvetica neue,Helvetica,Arial,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:jetbrains mono,Menlo,Monaco,Consolas,liberation mono,courier new,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::before,::after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-body);text-decoration:underline;font-weight:500;text-decoration-line:none}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-left:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=As]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=as]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=Is]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=is]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-left:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:#3b82f6}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:#3b82f6}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em;border:none;height:1px;background:radial-gradient(circle,#3b82f6 0%,#60a5fa 50%,#fcd34d,transparent)}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:regular;color:var(--tw-prose-body);border-left-width:.25rem;border-left-color:#64748b;quotes:none;margin-top:1.6em;margin-bottom:1.6em;padding-left:1em;padding-top:.0625rem;padding-right:.375rem;padding-bottom:.0625rem;border-radius:.1875rem .375rem .375rem .1875rem;background-color:#f1f5f9}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *))::before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *))::after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows)/10%),0 3px rgb(var(--tw-prose-kbd-shadows)/10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-right:.375em;padding-bottom:.1875em;padding-left:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:regular;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *))::before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *))::after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-right:1.1428571em;padding-bottom:.8571429em;padding-left:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *))::before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *))::after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;text-align:left;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders);background-color:#fcd34d;border:none}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-right:.5714286em;padding-bottom:.5714286em;padding-left:.5714286em;padding-top:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:#fcd34d}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-kbd:#111827;--tw-prose-kbd-shadows:17 24 39;--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:rgb(0 0 0 / 50%);--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>*:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>*:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>*:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>*:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-left:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-right:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-right:.5714286em;padding-bottom:.5714286em;padding-left:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-right:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose :where(p>code,li>code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-right:.375em;padding-bottom:.1875em;padding-left:.375em;background-color:#e2e8f0}.prose :where(p>code,li>code):not(:where([class~=not-prose],[class~=not-prose] *))::before{content:none}.prose :where(p>code,li>code):not(:where([class~=not-prose],[class~=not-prose] *))::after{content:none}.prose :where(em):not(:where([class~=not-prose],[class~=not-prose] *)){color:#3b82f6;font-family:Bellota,ui-serif,Georgia,Cambria,times new roman,Times,serif;letter-spacing:-.025em}.prose :where(thead th:first-child,tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:.5714286em}.prose :where(thead th:last-child,tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-right:.5714286em}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.9em;margin-bottom:1.6em;padding-left:1em;padding-top:.0625rem;padding-right:.0625rem;padding-bottom:.0625rem;border-radius:.1875rem .375rem .375rem .1875rem;position:relative;font-weight:500;border-left-width:.25rem}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)) .callout-icon{position:absolute;border-radius:50%;top:0;left:0;transform:translate(-50%,-50%);padding:.375rem;background-color:#fff}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).note{border-left-color:#6366f1;background-color:#e0e7ff}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).note .callout-icon{color:#6366f1}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).warning{border-left-color:#f43f5e;background-color:#ffe4e6}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).warning .callout-icon{color:#f43f5e}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).cta{border-left-color:#eab308;background-color:#fef9c3}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).cta .callout-icon{color:#eab308}.prose-sm :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.6666667em;margin-bottom:1.3333333em;padding-left:1.1111111em}.prose-lg :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.9166667em;margin-bottom:1.6666667em;padding-left:1em}.prose-xl :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.8em;margin-bottom:1.6em;padding-left:1.0666667em}.prose-2xl :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.9444444em;margin-bottom:1.7777778em;padding-left:1.1111111em}.prose-slate{--tw-prose-body:#334155;--tw-prose-headings:#0f172a;--tw-prose-lead:#475569;--tw-prose-links:#0f172a;--tw-prose-bold:#0f172a;--tw-prose-counters:#64748b;--tw-prose-bullets:#cbd5e1;--tw-prose-hr:#e2e8f0;--tw-prose-quotes:#0f172a;--tw-prose-quote-borders:#e2e8f0;--tw-prose-captions:#64748b;--tw-prose-kbd:#0f172a;--tw-prose-kbd-shadows:15 23 42;--tw-prose-code:#0f172a;--tw-prose-pre-code:#e2e8f0;--tw-prose-pre-bg:#1e293b;--tw-prose-th-borders:#cbd5e1;--tw-prose-td-borders:#e2e8f0;--tw-prose-invert-body:#cbd5e1;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#94a3b8;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#94a3b8;--tw-prose-invert-bullets:#475569;--tw-prose-invert-hr:#334155;--tw-prose-invert-quotes:#f1f5f9;--tw-prose-invert-quote-borders:#334155;--tw-prose-invert-captions:#94a3b8;--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#cbd5e1;--tw-prose-invert-pre-bg:rgb(0 0 0 / 50%);--tw-prose-invert-th-borders:#475569;--tw-prose-invert-td-borders:#334155}.absolute{position:absolute}.relative{position:relative}.-left-5{left:-1.25rem}.bottom-0{bottom:0}.left-0{left:0}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-6{margin-left:1.5rem;margin-right:1.5rem}.mx-auto{margin-left:auto;margin-right:auto}.-mt-2{margin-top:-.5rem}.mb-2{margin-bottom:.5rem}.mb-8{margin-bottom:2rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline{display:inline}.flex{display:flex}.table{display:table}.hidden{display:none}.size-5{width:1.25rem;height:1.25rem}.size-8{width:2rem;height:2rem}.h-\[37px\]{height:37px}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-6{width:1.5rem}.w-\[calc\(198\%_\+_1\.3px\)\]{width:calc(198% + 1.3px)}.w-full{width:100%}.max-w-max{max-width:max-content}.flex-1{flex:1}.flex-none{flex:none}.rotate-180{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-y-10{row-gap:2.5rem}.gap-y-2{row-gap:.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.border-b-2{border-bottom-width:2px}.border-amber-300{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity))}.border-transparent{border-color:transparent}.bg-amber-300{--tw-bg-opacity:1;background-color:rgb(252 211 77/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-\[linear-gradient\(120deg\2c theme\(colors\.blue\.500\)\2c theme\(colors\.blue\.400\)\2c theme\(colors\.amber\.300\)\2c theme\(colors\.blue\.400\)\2c theme\(colors\.blue\.500\)\)\]{background-image:linear-gradient(120deg,#3b82f6,#60a5fa,#fcd34d,#60a5fa,#3b82f6)}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.from-blue-500{--tw-gradient-from:#3b82f6 var(--tw-gradient-from-position);--tw-gradient-to:rgb(59 130 246 / 0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from), var(--tw-gradient-to)}.from-blue-800{--tw-gradient-from:#1e40af var(--tw-gradient-from-position);--tw-gradient-to:rgb(30 64 175 / 0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from), var(--tw-gradient-to)}.to-blue-500{--tw-gradient-to:#3b82f6 var(--tw-gradient-to-position)}.to-sky-600{--tw-gradient-to:#0284c7 var(--tw-gradient-to-position)}.bg-\[length\:100\%_0\.1rem\]{background-size:100% .1rem}.bg-\[length\:200\%\]{background-size:200%}.bg-left{background-position:0}.bg-left-bottom{background-position:0 100%}.bg-no-repeat{background-repeat:no-repeat}.stroke-amber-300{stroke:#fcd34d}.p-0{padding:0}.p-0\.5{padding:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pb-8{padding-bottom:2rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.text-center{text-align:center}.text-start{text-align:start}.text-end{text-align:end}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.lowercase{text-transform:lowercase}.leading-\[0\]{line-height:0}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.transition-\[background\]{transition-property:background;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.duration-300{transition-duration:300ms}.duration-500{transition-duration:500ms}@font-face{font-display:swap;font-family:nunito;font-style:normal;font-weight:400;src:url(/fonts/nunito-v14-regular.woff2)format('woff2')}@font-face{font-display:swap;font-family:nunito;font-style:italic;font-weight:400;src:url(/fonts/nunito-v14-italic.woff2)format('woff2')}@font-face{font-display:swap;font-family:nunito;font-style:normal;font-weight:600;src:url(/fonts/nunito-v14-600.woff2)format('woff2')}@font-face{font-display:swap;font-family:nunito;font-style:normal;font-weight:700;src:url(/fonts/nunito-v14-700.woff2)format('woff2')}@font-face{font-display:swap;font-family:nunito;font-style:italic;font-weight:700;src:url(/fonts/nunito-v14-700italic.woff2)format('woff2')}@font-face{font-display:swap;font-family:bellota;font-style:italic;font-weight:700;src:url(/fonts/Bellota-BoldItalic.woff2)format('woff2')}@font-face{font-display:swap;font-family:jetbrains mono;font-style:normal;font-weight:400;src:url(/fonts/JetBrainsMono-Regular.woff2)format('woff2')}@font-face{font-display:swap;font-family:jetbrains mono;font-style:normal;font-weight:700;src:url(/fonts/JetBrainsMono-Bold.woff2)format('woff2')}@font-face{font-display:swap;font-family:jetbrains mono;font-style:italic;font-weight:700;src:url(/fonts/JetBrainsMono-Italic.woff2)format('woff2')}@font-face{font-display:swap;font-family:jetbrains mono;font-style:italic;font-weight:700;src:url(/fonts/JetBrainsMono-BoldItalic.woff2)format('woff2')}h1,h2,h3,h4,h5,h6,p{text-rendering:optimizeLegibility}@media(min-width:768px){.md\:prose-lg{font-size:1.125rem;line-height:1.7777778}.md\:prose-lg :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em}.md\:prose-lg :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2222222em;line-height:1.4545455;margin-top:1.0909091em;margin-bottom:1.0909091em}.md\:prose-lg :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.6666667em;margin-bottom:1.6666667em;padding-left:1em}.md\:prose-lg :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.6666667em;margin-top:0;margin-bottom:.8333333em;line-height:1}.md\:prose-lg :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.6666667em;margin-top:1.8666667em;margin-bottom:1.0666667em;line-height:1.3333333}.md\:prose-lg :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.3333333em;margin-top:1.6666667em;margin-bottom:.6666667em;line-height:1.5}.md\:prose-lg :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:.4444444em;line-height:1.5555556}.md\:prose-lg :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.md\:prose-lg :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.md\:prose-lg :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.md\:prose-lg :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.md\:prose-lg :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;border-radius:.3125rem;padding-top:.2222222em;padding-right:.4444444em;padding-bottom:.2222222em;padding-left:.4444444em}.md\:prose-lg :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.md\:prose-lg :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8666667em}.md\:prose-lg :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em}.md\:prose-lg :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;line-height:1.75;margin-top:2em;margin-bottom:2em;border-radius:.375rem;padding-top:1em;padding-right:1.5em;padding-bottom:1em;padding-left:1.5em}.md\:prose-lg :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-left:1.5555556em}.md\:prose-lg :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-left:1.5555556em}.md\:prose-lg :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.6666667em;margin-bottom:.6666667em}.md\:prose-lg :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:.4444444em}.md\:prose-lg :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:.4444444em}.md\:prose-lg :where(.md\:prose-lg>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.8888889em;margin-bottom:.8888889em}.md\:prose-lg :where(.md\:prose-lg>ul>li>*:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.md\:prose-lg :where(.md\:prose-lg>ul>li>*:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.3333333em}.md\:prose-lg :where(.md\:prose-lg>ol>li>*:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.md\:prose-lg :where(.md\:prose-lg>ol>li>*:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.3333333em}.md\:prose-lg :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.8888889em;margin-bottom:.8888889em}.md\:prose-lg :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em}.md\:prose-lg :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.md\:prose-lg :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.6666667em;padding-left:1.5555556em}.md\:prose-lg :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:3.1111111em;margin-bottom:3.1111111em}.md\:prose-lg :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;line-height:1.5}.md\:prose-lg :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-right:.75em;padding-bottom:.75em;padding-left:.75em;padding-top:.75em}.md\:prose-lg :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:0}.md\:prose-lg :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-right:0}.md\:prose-lg :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.75em;padding-right:.75em;padding-bottom:.75em;padding-left:.75em}.md\:prose-lg :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:0}.md\:prose-lg :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-right:0}.md\:prose-lg :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.md\:prose-lg :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.md\:prose-lg :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;line-height:1.5;margin-top:1em}.md\:prose-lg :where(.md\:prose-lg>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(.md\:prose-lg>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.md\:prose-lg :where(p>code,li>code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;border-radius:.3125rem;padding-top:.2222222em;padding-right:.4444444em;padding-bottom:.2222222em;padding-left:.4444444em}.md\:prose-lg :where(thead th:first-child,tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:.75em}.md\:prose-lg :where(thead th:last-child,tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-right:.75em}.md\:prose-lg :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.9166667em;margin-bottom:1.6666667em;padding-left:1em}}.selection\:bg-blue-500 *::selection{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.selection\:text-white *::selection{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.selection\:bg-blue-500::selection{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.selection\:text-white::selection{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.before\:text-\[1\.05em\]::before{content:var(--tw-content);font-size:1.05em}.before\:text-blue-500::before{content:var(--tw-content);--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.before\:content-\[\'\[\[\'\]::before{--tw-content:'[[';content:var(--tw-content)}.after\:text-\[1\.05em\]::after{content:var(--tw-content);font-size:1.05em}.after\:text-blue-500::after{content:var(--tw-content);--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.after\:content-\[\'\]\]\'\]::after{--tw-content:']]';content:var(--tw-content)}.hover\:border-blue-500:hover{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.hover\:bg-gradient-to-b:hover{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.hover\:from-amber-300:hover{--tw-gradient-from:#fcd34d var(--tw-gradient-from-position);--tw-gradient-to:rgb(252 211 77 / 0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:to-amber-300:hover{--tw-gradient-to:#fcd34d var(--tw-gradient-to-position)}.hover\:bg-\[length\:100\%_100\%\]:hover{background-size:100% 100%}.hover\:bg-right:hover{background-position:100%}.hover\:text-blue-500:hover{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.focus\:text-blue-500:focus{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.peer\/nav:checked~.peer-checked\/nav\:block{display:block}.peer\/nav:checked~.peer-checked\/nav\:flex{display:flex}.peer\/nav:checked~.peer-checked\/nav\:hidden{display:none}@media(min-width:768px){.md\:mx-2{margin-left:.5rem;margin-right:.5rem}.md\:mx-auto{margin-left:auto;margin-right:auto}.md\:mb-12{margin-bottom:3rem}.md\:mt-0{margin-top:0}.md\:mt-10{margin-top:2.5rem}.md\:mt-16{margin-top:4rem}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-auto{width:auto}.md\:max-w-3xl{max-width:48rem}.md\:max-w-prose{max-width:65ch}.md\:flex-1{flex:1}.md\:flex-row{flex-direction:row}.md\:items-baseline{align-items:baseline}.md\:justify-end{justify-content:flex-end}.md\:justify-center{justify-content:center}.md\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px * var(--tw-space-y-reverse))}.md\:pb-0{padding-bottom:0}.md\:text-center{text-align:center}.md\:text-end{text-align:end}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-base{font-size:1rem;line-height:1.5rem}.md\:text-lg{font-size:1.125rem;line-height:1.75rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}.group:hover .md\:group-hover\:inline{display:inline}}@media(min-width:1024px){.lg\:max-w-4xl{max-width:56rem}.lg\:max-w-screen-md{max-width:768px}}.\[\&\>\*\]\:fill-white>*{fill:#fff} \ No newline at end of file diff --git a/css/main.min.955f98e6a86938f4ab69f779f0940b340a632dd3736119c2c6d199e36165b5fb.css b/css/main.min.955f98e6a86938f4ab69f779f0940b340a632dd3736119c2c6d199e36165b5fb.css new file mode 100644 index 00000000..a484d1e4 --- /dev/null +++ b/css/main.min.955f98e6a86938f4ab69f779f0940b340a632dd3736119c2c6d199e36165b5fb.css @@ -0,0 +1 @@ +*,::before,::after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,::before,::after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}::before,::after{--tw-content:''}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Nunito,Univers,Calibri,gill sans,gill sans mt,myriad pro,Myriad,dejavu sans condensed,liberation sans,nimbus sans l,Tahoma,Geneva,helvetica neue,Helvetica,Arial,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:jetbrains mono,Menlo,Monaco,Consolas,liberation mono,courier new,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-body);text-decoration:underline;font-weight:500;text-decoration-line:none}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=As]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=as]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=Is]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=is]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:#3b82f6}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:#3b82f6}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em;border:none;height:1px;background:radial-gradient(circle,#3b82f6 0%,#60a5fa 50%,#fcd34d,transparent)}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:regular;color:var(--tw-prose-body);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:none;margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em;padding-top:.0625rem;padding-right:.375rem;padding-bottom:.0625rem;border-radius:.1875rem .375rem .375rem .1875rem;border-left-color:#64748b;background-color:#f1f5f9}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *))::before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *))::after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows)/10%),0 3px rgb(var(--tw-prose-kbd-shadows)/10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:regular;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *))::before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *))::after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *))::before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *))::after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders);background-color:#fcd34d;border:none}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em;padding-top:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:#fcd34d}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-kbd:#111827;--tw-prose-kbd-shadows:17 24 39;--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:rgb(0 0 0 / 50%);--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose :where(p>code,li>code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-right:.375em;padding-bottom:.1875em;padding-left:.375em;background-color:#e2e8f0}.prose :where(p>code,li>code):not(:where([class~=not-prose],[class~=not-prose] *))::before{content:none}.prose :where(p>code,li>code):not(:where([class~=not-prose],[class~=not-prose] *))::after{content:none}.prose :where(em):not(:where([class~=not-prose],[class~=not-prose] *)){color:#3b82f6;font-family:Bellota,ui-serif,Georgia,Cambria,times new roman,Times,serif;letter-spacing:-.025em}.prose :where(thead th:first-child,tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:.5714286em}.prose :where(thead th:last-child,tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-right:.5714286em}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.9em;margin-bottom:1.6em;padding-left:1em;padding-top:.0625rem;padding-right:.0625rem;padding-bottom:.0625rem;border-radius:.1875rem .375rem .375rem .1875rem;position:relative;font-weight:500;border-left-width:.25rem}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)) .callout-icon{position:absolute;border-radius:50%;top:0;left:0;transform:translate(-50%,-50%);padding:.375rem;background-color:#fff}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).note{border-left-color:#6366f1;background-color:#e0e7ff}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).note .callout-icon{color:#6366f1}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).warning{border-left-color:#f43f5e;background-color:#ffe4e6}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).warning .callout-icon{color:#f43f5e}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).cta{border-left-color:#eab308;background-color:#fef9c3}.prose :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)).cta .callout-icon{color:#eab308}.prose-sm :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.6666667em;margin-bottom:1.3333333em;padding-left:1.1111111em}.prose-lg :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.9166667em;margin-bottom:1.6666667em;padding-left:1em}.prose-xl :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.8em;margin-bottom:1.6em;padding-left:1.0666667em}.prose-2xl :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.9444444em;margin-bottom:1.7777778em;padding-left:1.1111111em}.prose-slate{--tw-prose-body:#334155;--tw-prose-headings:#0f172a;--tw-prose-lead:#475569;--tw-prose-links:#0f172a;--tw-prose-bold:#0f172a;--tw-prose-counters:#64748b;--tw-prose-bullets:#cbd5e1;--tw-prose-hr:#e2e8f0;--tw-prose-quotes:#0f172a;--tw-prose-quote-borders:#e2e8f0;--tw-prose-captions:#64748b;--tw-prose-kbd:#0f172a;--tw-prose-kbd-shadows:15 23 42;--tw-prose-code:#0f172a;--tw-prose-pre-code:#e2e8f0;--tw-prose-pre-bg:#1e293b;--tw-prose-th-borders:#cbd5e1;--tw-prose-td-borders:#e2e8f0;--tw-prose-invert-body:#cbd5e1;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#94a3b8;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#94a3b8;--tw-prose-invert-bullets:#475569;--tw-prose-invert-hr:#334155;--tw-prose-invert-quotes:#f1f5f9;--tw-prose-invert-quote-borders:#334155;--tw-prose-invert-captions:#94a3b8;--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#cbd5e1;--tw-prose-invert-pre-bg:rgb(0 0 0 / 50%);--tw-prose-invert-th-borders:#475569;--tw-prose-invert-td-borders:#334155}.absolute{position:absolute}.relative{position:relative}.-left-5{left:-1.25rem}.bottom-0{bottom:0}.left-0{left:0}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-6{margin-left:1.5rem;margin-right:1.5rem}.mx-auto{margin-left:auto;margin-right:auto}.-mt-2{margin-top:-.5rem}.mb-2{margin-bottom:.5rem}.mb-8{margin-bottom:2rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline{display:inline}.flex{display:flex}.table{display:table}.hidden{display:none}.size-5{width:1.25rem;height:1.25rem}.size-8{width:2rem;height:2rem}.h-\[37px\]{height:37px}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-6{width:1.5rem}.w-\[calc\(198\%_\+_1\.3px\)\]{width:calc(198% + 1.3px)}.w-full{width:100%}.max-w-max{max-width:max-content}.flex-1{flex:1}.flex-none{flex:none}.rotate-180{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-y-10{row-gap:2.5rem}.gap-y-2{row-gap:.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.border-b-2{border-bottom-width:2px}.border-amber-300{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity))}.border-transparent{border-color:transparent}.bg-amber-300{--tw-bg-opacity:1;background-color:rgb(252 211 77/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-\[linear-gradient\(120deg\2c theme\(colors\.blue\.500\)\2c theme\(colors\.blue\.400\)\2c theme\(colors\.amber\.300\)\2c theme\(colors\.blue\.400\)\2c theme\(colors\.blue\.500\)\)\]{background-image:linear-gradient(120deg,#3b82f6,#60a5fa,#fcd34d,#60a5fa,#3b82f6)}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.from-blue-500{--tw-gradient-from:#3b82f6 var(--tw-gradient-from-position);--tw-gradient-to:rgb(59 130 246 / 0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from), var(--tw-gradient-to)}.from-blue-800{--tw-gradient-from:#1e40af var(--tw-gradient-from-position);--tw-gradient-to:rgb(30 64 175 / 0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from), var(--tw-gradient-to)}.to-blue-500{--tw-gradient-to:#3b82f6 var(--tw-gradient-to-position)}.to-sky-600{--tw-gradient-to:#0284c7 var(--tw-gradient-to-position)}.bg-\[length\:100\%_0\.1rem\]{background-size:100% .1rem}.bg-\[length\:200\%\]{background-size:200%}.bg-left{background-position:0}.bg-left-bottom{background-position:0 100%}.bg-no-repeat{background-repeat:no-repeat}.stroke-amber-300{stroke:#fcd34d}.p-0\.5{padding:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pb-8{padding-bottom:2rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.text-center{text-align:center}.text-start{text-align:start}.text-end{text-align:end}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.lowercase{text-transform:lowercase}.leading-\[0\]{line-height:0}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.transition-\[background\]{transition-property:background;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.duration-300{transition-duration:300ms}.duration-500{transition-duration:500ms}@font-face{font-display:swap;font-family:nunito;font-style:normal;font-weight:400;src:url(/fonts/nunito-v14-regular.woff2)format('woff2')}@font-face{font-display:swap;font-family:nunito;font-style:italic;font-weight:400;src:url(/fonts/nunito-v14-italic.woff2)format('woff2')}@font-face{font-display:swap;font-family:nunito;font-style:normal;font-weight:600;src:url(/fonts/nunito-v14-600.woff2)format('woff2')}@font-face{font-display:swap;font-family:nunito;font-style:normal;font-weight:700;src:url(/fonts/nunito-v14-700.woff2)format('woff2')}@font-face{font-display:swap;font-family:nunito;font-style:italic;font-weight:700;src:url(/fonts/nunito-v14-700italic.woff2)format('woff2')}@font-face{font-display:swap;font-family:bellota;font-style:italic;font-weight:700;src:url(/fonts/Bellota-BoldItalic.woff2)format('woff2')}@font-face{font-display:swap;font-family:jetbrains mono;font-style:normal;font-weight:400;src:url(/fonts/JetBrainsMono-Regular.woff2)format('woff2')}@font-face{font-display:swap;font-family:jetbrains mono;font-style:normal;font-weight:700;src:url(/fonts/JetBrainsMono-Bold.woff2)format('woff2')}@font-face{font-display:swap;font-family:jetbrains mono;font-style:italic;font-weight:700;src:url(/fonts/JetBrainsMono-Italic.woff2)format('woff2')}@font-face{font-display:swap;font-family:jetbrains mono;font-style:italic;font-weight:700;src:url(/fonts/JetBrainsMono-BoldItalic.woff2)format('woff2')}h1,h2,h3,h4,h5,h6,p{text-rendering:optimizeLegibility}@media(min-width:768px){.md\:prose-lg{font-size:1.125rem;line-height:1.7777778}.md\:prose-lg :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em}.md\:prose-lg :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2222222em;line-height:1.4545455;margin-top:1.0909091em;margin-bottom:1.0909091em}.md\:prose-lg :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.6666667em;margin-bottom:1.6666667em;padding-inline-start:1em}.md\:prose-lg :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.6666667em;margin-top:0;margin-bottom:.8333333em;line-height:1}.md\:prose-lg :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.6666667em;margin-top:1.8666667em;margin-bottom:1.0666667em;line-height:1.3333333}.md\:prose-lg :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.3333333em;margin-top:1.6666667em;margin-bottom:.6666667em;line-height:1.5}.md\:prose-lg :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:.4444444em;line-height:1.5555556}.md\:prose-lg :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.md\:prose-lg :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.md\:prose-lg :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.md\:prose-lg :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.md\:prose-lg :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;border-radius:.3125rem;padding-top:.2222222em;padding-inline-end:.4444444em;padding-bottom:.2222222em;padding-inline-start:.4444444em}.md\:prose-lg :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.md\:prose-lg :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8666667em}.md\:prose-lg :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em}.md\:prose-lg :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;line-height:1.75;margin-top:2em;margin-bottom:2em;border-radius:.375rem;padding-top:1em;padding-inline-end:1.5em;padding-bottom:1em;padding-inline-start:1.5em}.md\:prose-lg :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.5555556em}.md\:prose-lg :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.5555556em}.md\:prose-lg :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.6666667em;margin-bottom:.6666667em}.md\:prose-lg :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4444444em}.md\:prose-lg :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4444444em}.md\:prose-lg :where(.md\:prose-lg>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.8888889em;margin-bottom:.8888889em}.md\:prose-lg :where(.md\:prose-lg>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.md\:prose-lg :where(.md\:prose-lg>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.3333333em}.md\:prose-lg :where(.md\:prose-lg>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.md\:prose-lg :where(.md\:prose-lg>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.3333333em}.md\:prose-lg :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.8888889em;margin-bottom:.8888889em}.md\:prose-lg :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em}.md\:prose-lg :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.md\:prose-lg :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.6666667em;padding-inline-start:1.5555556em}.md\:prose-lg :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:3.1111111em;margin-bottom:3.1111111em}.md\:prose-lg :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;line-height:1.5}.md\:prose-lg :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:.75em;padding-bottom:.75em;padding-inline-start:.75em;padding-top:.75em}.md\:prose-lg :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.md\:prose-lg :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.md\:prose-lg :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.75em;padding-inline-end:.75em;padding-bottom:.75em;padding-inline-start:.75em}.md\:prose-lg :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.md\:prose-lg :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.md\:prose-lg :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.md\:prose-lg :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.md\:prose-lg :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;line-height:1.5;margin-top:1em}.md\:prose-lg :where(.md\:prose-lg>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose-lg :where(.md\:prose-lg>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.md\:prose-lg :where(p>code,li>code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;border-radius:.3125rem;padding-top:.2222222em;padding-right:.4444444em;padding-bottom:.2222222em;padding-left:.4444444em}.md\:prose-lg :where(thead th:first-child,tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-left:.75em}.md\:prose-lg :where(thead th:last-child,tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-right:.75em}.md\:prose-lg :where(.callout):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.9166667em;margin-bottom:1.6666667em;padding-left:1em}}.selection\:bg-blue-500 *::selection{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.selection\:text-white *::selection{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.selection\:bg-blue-500::selection{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.selection\:text-white::selection{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.before\:text-\[1\.05em\]::before{content:var(--tw-content);font-size:1.05em}.before\:text-blue-500::before{content:var(--tw-content);--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.before\:content-\[\'\[\[\'\]::before{--tw-content:'[[';content:var(--tw-content)}.after\:text-\[1\.05em\]::after{content:var(--tw-content);font-size:1.05em}.after\:text-blue-500::after{content:var(--tw-content);--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.after\:content-\[\'\]\]\'\]::after{--tw-content:']]';content:var(--tw-content)}.hover\:border-blue-500:hover{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.hover\:bg-gradient-to-b:hover{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.hover\:from-amber-300:hover{--tw-gradient-from:#fcd34d var(--tw-gradient-from-position);--tw-gradient-to:rgb(252 211 77 / 0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:to-amber-300:hover{--tw-gradient-to:#fcd34d var(--tw-gradient-to-position)}.hover\:bg-\[length\:100\%_100\%\]:hover{background-size:100% 100%}.hover\:bg-right:hover{background-position:100%}.hover\:text-blue-500:hover{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.focus\:text-blue-500:focus{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.peer\/nav:checked~.peer-checked\/nav\:block{display:block}.peer\/nav:checked~.peer-checked\/nav\:flex{display:flex}.peer\/nav:checked~.peer-checked\/nav\:hidden{display:none}@media(min-width:768px){.md\:mx-2{margin-left:.5rem;margin-right:.5rem}.md\:mx-auto{margin-left:auto;margin-right:auto}.md\:mb-12{margin-bottom:3rem}.md\:mt-0{margin-top:0}.md\:mt-10{margin-top:2.5rem}.md\:mt-16{margin-top:4rem}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-auto{width:auto}.md\:max-w-3xl{max-width:48rem}.md\:max-w-prose{max-width:65ch}.md\:flex-1{flex:1}.md\:flex-row{flex-direction:row}.md\:items-baseline{align-items:baseline}.md\:justify-end{justify-content:flex-end}.md\:justify-center{justify-content:center}.md\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px * var(--tw-space-y-reverse))}.md\:pb-0{padding-bottom:0}.md\:text-center{text-align:center}.md\:text-end{text-align:end}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-base{font-size:1rem;line-height:1.5rem}.md\:text-lg{font-size:1.125rem;line-height:1.75rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}.group:hover .md\:group-hover\:inline{display:inline}}@media(min-width:1024px){.lg\:max-w-4xl{max-width:56rem}.lg\:max-w-screen-md{max-width:768px}}.\[\&\>\*\]\:fill-white>*{fill:#fff} \ No newline at end of file diff --git a/feeds/all.atom.xml b/feeds/all.atom.xml index 1b37c21a..66b44063 100644 --- a/feeds/all.atom.xml +++ b/feeds/all.atom.xml @@ -12,8 +12,18 @@ 2024-03-16T14:35:00Z Yuliya Bagriytag:www.aviskase.com,2024-03-16:/articles/2024/03/16/goodbye-ogimage/ - + When you see a link on almost all social platforms or messengers, you usually see a card preview with a title, an almost invisible summary, and an image. These cards are generated based on twitter and Open Graph HTML tags that look something like this:

    +
    <meta property="og:url" content="<url>">
    +<meta property="og:title" content="<title>">
    +<meta property="og:description" content="<description>">
    +<meta property="og:image" content="<link>">
    +
    +<meta name="twitter:card" content="summary_large_image">
    +<meta property="twitter:url" content="<url>">
    +<meta name="twitter:title" content="<title>">
    +<meta name="twitter:description" content="<description>">
    +<meta name="twitter:image" content="<link>">
    +

    See those image properties? Per specifications they are required. The minimum size is 200×200 pixels; the recommended size is 1200×630 pixels.

    ]]>
    When you see a link on almost all social platforms or messengers, you usually see a card preview with a title, an almost invisible summary, and an image. These cards are generated based on twitter and Open Graph HTML tags that look something like this:

    <meta property="og:url" content="<url>">
     <meta property="og:title" content="<title>">
    @@ -47,9 +57,18 @@
         2024-02-24T03:18:00Z
         Yuliya Bagriytag:www.aviskase.com,2024-02-24:/articles/2024/02/23/two-ten-years-of-february/
         
    -    
    +    Today I’d like to share this bit about my great-grand father Bagriy Ivan:

    +
    +

    The last bunker in our region was destroyed by the NKVD in 1952 in the Lubianets forest. +Four shylyans were killed then:

    +
      +
    • Antoniuk Volodymyr - born in 1923.
    • +
    • Bagriy Ivan - born in 1911.
    • +
    • Voznyi Ivan - born in 1905.
    • +
    • Hulovskyi Vasyl - born in 1918.
    • +
    +
    +

    Nineteen fifty-two. By that time, Ivan’s wife and son (my grandfather) had already been exiled to Kazakhstan.

    ]]>
    Today I’d like to share this bit about my great-grand father Bagriy Ivan:

    The last bunker in our region was destroyed by the NKVD in 1952 in the Lubianets forest. @@ -73,8 +92,9 @@ Four shylyans were killed then:

    2024-01-26T04:59:46Z Yuliya Bagriytag:www.aviskase.com,2024-01-26:/articles/2024/01/25/open-banking-canada-vs-kazakhstan/ - + As I was skimming through The API Changelog issue 2024.04, my eye caught:

    +
    +

    Kazakhstan’s National Bank and other entities published a report on an Open API and Open Banking pilot, aiming to modernize financial services through standardized data exchange. The successful pilot involved multiple banks and clients, setting the stage for a transparent and competitive financial sector by 2024.

    ]]>
    As I was skimming through The API Changelog issue 2024.04, my eye caught:

    Kazakhstan’s National Bank and other entities published a report on an Open API and Open Banking pilot, aiming to modernize financial services through standardized data exchange. The successful pilot involved multiple banks and clients, setting the stage for a transparent and competitive financial sector by 2024.

    @@ -106,9 +126,9 @@ Kazakhstan’s National Bank and other entities published a report on an Ope 2023-12-15T05:10:48Z Yuliya Bagriytag:www.aviskase.com,2023-12-15:/articles/2023/12/15/the-navys-it-challenges/ - + Can’t stop thinking about one line from the recent Paul Well’s interview with Vice Admiral Angus Topshee, the Commander of the Royal Canadian Navy.

    +
    +

    PW: Is the fact that this video’s on YouTube a reflection of any difficulty you’re having getting heard internally?

    ]]>
    Can’t stop thinking about one line from the recent Paul Well’s interview with Vice Admiral Angus Topshee, the Commander of the Royal Canadian Navy.

    PW: Is the fact that this video’s on YouTube a reflection of any difficulty you’re having getting heard internally?

    @@ -129,8 +149,9 @@ AT: No. I’ve shared my assessments with the leadership of the Department, 2021-09-06T20:35:43Z Yuliya Bagriytag:www.aviskase.com,2021-09-06:/articles/2021/09/06/using-openapi-cli-custom-rules/ - + If you’re planning to lint OpenAPI description documents (you should!), always check whether a linter supports adding custom rules. And I mean not just changing severity or disabling predefined rules, but actually adding new ones specific to your API standards.

    ]]>
    If you’re planning to lint OpenAPI description documents (you should!), always check whether a linter supports adding custom rules. And I mean not just changing severity or disabling predefined rules, but actually adding new ones specific to your API standards.

    @@ -331,8 +352,8 @@ openapi-cli, as any respectable OpenAPI linter, allows that. The process is very 2021-08-17T03:44:37Z Yuliya Bagriytag:www.aviskase.com,2021-08-17:/articles/2021/08/16/using-openapi-cli-custom-preprocessing/ - + The key feature of openapi-cli is its extensibility. There are three ways to extend it: preprocessors, rules, and decorators. In comparison, Spectral supports only custom rules.

    +

    In this tutorial, we’ll start with preprocessors. They are used to transform OpenAPI description document before validation and linting. Mind you, documentation says they should be avoided, because custom preprocessors tend to be error prone.

    ]]>
    The key feature of openapi-cli is its extensibility. There are three ways to extend it: preprocessors, rules, and decorators. In comparison, Spectral supports only custom rules.

    In this tutorial, we’ll start with preprocessors. They are used to transform OpenAPI description document before validation and linting. Mind you, documentation says they should be avoided, because custom preprocessors tend to be error prone.

    @@ -434,7 +455,7 @@ In this tutorial, we’ll start with preprocessors. They are used to transfo - + Redoc with modified descriptions for extensible enumerations @@ -456,8 +477,13 @@ In this tutorial, we’ll start with preprocessors. They are used to transfo 2021-03-23T04:30:57Z Yuliya Bagriytag:www.aviskase.com,2021-03-23:/articles/2021/03/23/using-openapi-cli-during-api-design-part-two/ - + Part one showed the basics of using openapi-cli for a single-definition project. Let’s see how it works for multi-definition projects.

    +

    + + Multi-what? +

    +

    By multi-definition project I mean a project with several OpenAPI description documents. For example, it can be useful if you want to keep contracts for all services in one place. Then, you can reuse common schemas between definitions.

    ]]>
    Part one showed the basics of using openapi-cli for a single-definition project. Let’s see how it works for multi-definition projects.

    @@ -523,8 +549,12 @@ To preview a specific definition, you need to pass a definition label from confi 2021-02-01T01:57:57Z Yuliya Bagriytag:www.aviskase.com,2021-02-01:/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/ - + Obviously, API design is much more than writing OpenAPI description doc. First of all, should it even be OpenAPI-based? If yes, using openapi-cli will make your life a bit easier.

    +

    + + Example API +

    +

    Let’s imagine you’ve been asked to create an API for Stargate Network and you already did some preliminary analysis.

    ]]>
    Obviously, API design is much more than writing OpenAPI description doc. First of all, should it even be OpenAPI-based? If yes, using openapi-cli will make your life a bit easier.

    @@ -579,7 +609,7 @@ To preview a specific definition, you need to pass a definition label from confi - + Output of create-openapi-repo tool @@ -612,7 +642,7 @@ To preview a specific definition, you need to pass a definition label from confi - + Comparison of property display with requiredPropsFirst equal to false or true @@ -706,8 +736,18 @@ To preview a specific definition, you need to pass a definition label from confi 2021-01-10T19:28:31Z Yuliya Bagriytag:www.aviskase.com,2021-01-10:/articles/2021/01/10/using-openapi-cli-for-api-exploration/ - + As promised, let’s dive into the usage of openapi-cli. The first topic is semi-non-technical: API exploration. You might be interested if:

    +
      +
    • you’re a tech writer
    • +
    • you’re a tester
    • +
    • you don’t know what the heck API exploration is
    • +
    +

    + + What is API exploration +

    +

    I use this term akin to exploratory testing. You can search for works of Cem Kaner, James Bach, Michael Bolton, Elisabeth Hendrickson, James Whittaker, but beware, that’s a nice little serpentarium of a field.

    ]]>
    As promised, let’s dive into the usage of openapi-cli. The first topic is semi-non-technical: API exploration. You might be interested if:

      @@ -838,9 +878,27 @@ you’re a tech writer you’re a tester you don’t know what the h 2020-12-30T14:35:16Z Yuliya Bagriytag:www.aviskase.com,2020-12-30:/articles/2020/12/30/hmms-2020/ - + Usually it’s monthly collection of things that got me thinking hmming. But this is the end of the year, let’s do a little retrospective.

      +

      + + Retro +

      +

      What happened this year:

      +
        +
      • My title switched from “tester” to “engineer”. Yay?
      • +
      • Had a great time at ASC 2020 conference. Yes, it’s not my first conference, but this one left a good long-lasting aftertaste.
      • +
      • I ported blog from pelican to hugo and started a redesign. Still much to do and polish, but at least the base is sound.
      • +
      • Joined Obsidian community.
      • +
      • As a consequence, I unwillingly created a YouTube channel in Russian. It’s cool to learn new skills (audio cleaning, video editing), but to be honest, content creation in Russian isn’t exactly a lucrative or professionally valuable hobby. Still, I cannot say no to people asking to teach or explain something: knowledge sharing is one of the biggest values to me.
      • +
      • Finished Finnish tree on Duolingo. Don’t ask why, I, person living in Quebec, learn Finnish. I just want to! Se on täydellinen kieli.
      • +
      +

      What didn’t happen:

      ]]>
      Usually it’s monthly collection of things that got me thinking hmming. But this is the end of the year, let’s do a little retrospective.

      @@ -888,7 +946,7 @@ My title switched from “tester” to “engineer”. Yay? Had - + Advent of Code easter egg: using itertools @@ -902,10 +960,9 @@ My title switched from “tester” to “engineer”. Yay? Had 2020-12-21T03:26:21Z Yuliya Bagriytag:www.aviskase.com,2020-12-21:/articles/2020/12/20/using-openapi-cli-intro/ - + Before we jump into the usage of this mysterious tool with way too generic name, let me give you an introduction.

      +

      There are many tools for supporting API design via OpenAPI description documents. You can check the list here.

      +

      The most common tasks are:

      ]]>
      Before we jump into the usage of this mysterious tool with way too generic name, let me give you an introduction.

      There are many tools for supporting API design via OpenAPI description documents. You can check the list here.

      The most common tasks are:

      @@ -947,7 +1004,8 @@ dereferencing linting reference documentation preview and/or generation I talked 2020-12-08T01:59:28Z Yuliya Bagriytag:www.aviskase.com,2020-12-08:/articles/2020/12/07/crash-course-into-api-related-terminology/ - + Anyone trying to dive into the world of APIs is doomed to be confused by conflicting terminology. In this post I’m gonna touch upon the main definitions and processes. I already tried it once during lunch&learn session at work, but the time has come to revisit that old presentation in the written form.

      ]]>
      Anyone trying to dive into the world of APIs is doomed to be confused by conflicting terminology. In this post I’m gonna touch upon the main definitions and processes. I already tried it once during lunch&learn session at work, but the time has come to revisit that old presentation in the written form.

      One last note: the basis for my definitions goes from OpenAPI Glossary started by Phil Sturgeon.

      @@ -1044,9 +1102,13 @@ dereferencing linting reference documentation preview and/or generation I talked 2020-11-30T23:35:10Z Yuliya Bagriytag:www.aviskase.com,2020-11-30:/articles/2020/11/30/hmms-november/ - + Monthly collection of things that got me thinking hmming.

      +

      + + Awards and reports +

      +

      Time for ThoughtWorks Technology Radar. One of the themes is democratizing programming which is tightly coupled with democratization of APIs I mentioned in the last hmms.

      ]]>
      Monthly collection of things that got me thinking hmming.

      @@ -1101,8 +1163,15 @@ The 2020 state of DevOps report is there! I haven’t even read it yet =)]]> 2020-11-04T03:33:09Z Yuliya Bagriytag:www.aviskase.com,2020-11-04:/articles/2020/11/03/hmms-october/ - + Monthly collection of things that got me thinking hmming.

      +
      +
      Monthly collection of things that got me thinking hmming.

      +

      Since migrating to Obsidian for knowledge management, I’ve lost a track of interesting resources I read, because they are represented as atomic cross-referencing notes.

      +

      Perhaps I should try to be more consistent and always write literature notes. Alas, my current graph looks like that (obviously, I didn’t have time to transfer older notes from other systems):

      ]]> Oops, I have nothing to share @@ -1636,7 +1721,7 @@ Perhaps I should try to be more consistent and always write literature notes. Al - + Obsidian graph view as of July 2020 @@ -1671,8 +1756,12 @@ Perhaps I should try to be more consistent and always write literature notes. Al 2020-07-03T19:50:58Z Yuliya Bagriytag:www.aviskase.com,2020-07-03:/articles/2020/07/03/knowledge-management-tools/ - + As a knowledge worker, I spend a lot of time searching and experimenting with approaches for knowledge capture and storage. Currently, I play with the new shiny app, so I figured it’s a perfect moment to systemize and reflect on tools and techniques I used until now.

      +

      + + Handwritten +

      +

      I was more into handwriting during school and university, and there are still tons of notebooks back in Kazakhstan with course notes. I followed the same process and structure for extra studies like Coursera, and the most important thing I learned was that these notes were never ever opened and referenced later. Useless waste of stationery.

      ]]>
      As a knowledge worker, I spend a lot of time searching and experimenting with approaches for knowledge capture and storage. Currently, I play with the new shiny app, so I figured it’s a perfect moment to systemize and reflect on tools and techniques I used until now.

      @@ -1685,7 +1774,7 @@ Perhaps I should try to be more consistent and always write literature notes. Al - + Old handwritten notebook @@ -1697,7 +1786,7 @@ Perhaps I should try to be more consistent and always write literature notes. Al - + Untranslatable scribbles @@ -1708,7 +1797,7 @@ Perhaps I should try to be more consistent and always write literature notes. Al - + Brainstorming in Rocketbook @@ -1728,7 +1817,7 @@ Perhaps I should try to be more consistent and always write literature notes. Al - + Notes in the Google Docs @@ -1744,7 +1833,7 @@ Perhaps I should try to be more consistent and always write literature notes. Al - + Sublime with PlainNotes @@ -1766,7 +1855,7 @@ Perhaps I should try to be more consistent and always write literature notes. Al - + XMind mind map overview @@ -1802,7 +1891,7 @@ Perhaps I should try to be more consistent and always write literature notes. Al - + Google Keep with transient lists @@ -1869,9 +1958,12 @@ Perhaps I should try to be more consistent and always write literature notes. Al 2020-07-01T19:10:45Z Yuliya Bagriytag:www.aviskase.com,2020-07-01:/articles/2020/07/01/hmms-june/ - + Time for the list of things I found interesting/amusing this month.

      +
      +

      Let’s start with the good news: OpenAPI 3.1.0 RC is out!. Webhooks and reconciliation with JSON Schema are the highlights of the version. I hope tooling support won’t lag this time.

      ]]>
      Time for the list of things I found interesting/amusing this month.

      +

      One of the good things to emerge during the corona times is more educational opportunities.

      +

      For example, you can (and perhaps should) check the +“Advanced Distributed Systems Design” course by Udi Dahan.

      +

      Another way to satisfy knowledge thirst is to attend a virtual conference:

      ]]> Learning @@ -2092,9 +2192,10 @@ not just generic NDA-sanitized dev blog posts.

      2020-04-22T01:06:01Z Yuliya Bagriytag:www.aviskase.com,2020-04-22:/articles/2020/04/22/using-insomnia-for-api-exploration/ - + One of the tools I use almost daily is Insomnia. +It’s a great alternative to the P-everyone-knows-that-one. +Insomnia is easy to use on Linux, has plugins, and UI is clean and simple.

      +

      Let me show you some basic features. We will use OpenWeatherMap API.

      ]]>
      One of the tools I use almost daily is Insomnia. It’s a great alternative to the P-everyone-knows-that-one. Insomnia is easy to use on Linux, has plugins, and UI is clean and simple.

      @@ -2112,7 +2213,7 @@ so you need to add it to the query parameters for each request.

      - + Insomnia: basic interface @@ -2142,7 +2243,7 @@ because we have access only to one server.

      - + Insomnia: base environment @@ -2153,7 +2254,7 @@ because we have access only to one server.

      - + Insomnia: sub environment @@ -2164,7 +2265,7 @@ because we have access only to one server.

      - + Insomnia: folder-level environment @@ -2175,7 +2276,7 @@ because we have access only to one server.

      - + Insomnia: requests with variables from environments @@ -2189,7 +2290,7 @@ This plugin allows us to set default headers and/or query parameters in the envi - + Insomnia: environment setup with query param defaults @@ -2200,7 +2301,7 @@ This plugin allows us to set default headers and/or query parameters in the envi - + Insomnia: requests with default query params @@ -2218,7 +2319,7 @@ array that is a property of elements in the list array’. - + Insomnia: querying response using JSONPath @@ -2237,7 +2338,7 @@ which gives you tons of options.

      - + Insomnia: random value generation @@ -2247,7 +2348,7 @@ which gives you tons of options.

      - + Insomnia: usage of generated values in the request @@ -2265,7 +2366,7 @@ In Insomnia you should use Response template tag and JSONPath if yo - + Insomnia: pulling values from previous response @@ -2275,7 +2376,7 @@ In Insomnia you should use Response template tag and JSONPath if yo - + Insomnia: request with pulled values @@ -2292,10 +2393,29 @@ don’t try to turn it into a complex automation solution. That’s what 2020-03-31T20:24:05Z Yuliya Bagriytag:www.aviskase.com,2020-03-31:/articles/2020/03/31/hmms-march/ - + Coronavirus-free edition!

      +

      + + Top X videos and readings +

      +

      +

      + + + + + Meme: APIs, APIs everywhere + + +

      +
      Coronavirus-free edition!

      @@ -2306,7 +2426,7 @@ Another chose to build their own API gateway because no offerings existed that w - + Meme: APIs, APIs everywhere @@ -2375,8 +2495,15 @@ and/or flexible hours positions (which are rare at the moment)?

      2020-03-08T00:35:17Z Yuliya Bagriytag:www.aviskase.com,2020-03-08:/articles/2020/03/08/hmms-february/ - +
      It’s March already, time to summarize February!

      +

      + + APIs trainings +

      +

      I did a small company-wide training about APIs. Here is the main deck +and supplementary deck left from dev lunch&learn. +Anyone is free to use it and if you have questions just ping me somewhere. They don’t have notes, so can be pretty +cryptic, but I am inclined to continue improving them to use for new hires.

      ]]>
      It’s March already, time to summarize February!

      @@ -2422,7 +2549,11 @@ dive in.

      2020-02-07T23:40:44Z Yuliya Bagriytag:www.aviskase.com,2020-02-07:/articles/2020/02/07/api-testing-in-python-requests-vs-bravado/ - +
      +

      This article is written as a result of collaboration with TestProject. +While many of you know me as a GUI-driven tools hater, +that’s just my preference, so if something works for you and your company, that’s the only thing +that matters. There are no best practices and there are no best tools for everyone.

      ]]>

      This article is written as a result of collaboration with TestProject. While many of you know me as a GUI-driven tools hater, @@ -2614,7 +2745,9 @@ libraries and consider different alternatives.

      2020-02-01T23:16:38Z Yuliya Bagriytag:www.aviskase.com,2020-02-01:/articles/2020/02/01/hmms-january/ - + Hi! Weekly hmms are transformed into monthly hmms, mostly because writing mandatory weekly posts is too cumbersome +and leaves no time to do more thematic writing. Of course, there are other reasons too. Almost all winter holidays +I had a cold (TWICE!) and being ill isn’t the best motivation ever.

      ]]>
      Hi! Weekly hmms are transformed into monthly hmms, mostly because writing mandatory weekly posts is too cumbersome and leaves no time to do more thematic writing. Of course, there are other reasons too. Almost all winter holidays I had a cold (TWICE!) and being ill isn’t the best motivation ever.

      @@ -2670,8 +2803,8 @@ Even though I never played Skyrim or Morrowind, TES wiki is an awesome read for 2019-12-26T19:21:00Z Yuliya Bagriytag:www.aviskase.com,2019-12-26:/articles/2019/12/26/lunchlearn-linting-openapi-description-docs/ - + Last yearly company retrospective showed that we want to be better at knowledge sharing. One of the suggested formats is Lunch&Learn, kinda like Google’s Testing on the Toilet: short non-mandatory meeting during the lunchtime (30min) with shared recording later.

      +

      I’ve started to use them for API related topics. Tools, terminology, and other small but important things that would be beneficial to most of the development team (FYI, testers are part of the dev team, duh). Also, it’s an opportunity to organize my brain, so I’ll be posting notes here for future reference. One thing though, these posts are not supposed to be comprehensive: there is only information applicable to my team!

      ]]>
      Last yearly company retrospective showed that we want to be better at knowledge sharing. One of the suggested formats is Lunch&Learn, kinda like Google’s Testing on the Toilet: short non-mandatory meeting during the lunchtime (30min) with shared recording later.

      I’ve started to use them for API related topics. Tools, terminology, and other small but important things that would be beneficial to most of the development team (FYI, testers are part of the dev team, duh). Also, it’s an opportunity to organize my brain, so I’ll be posting notes here for future reference. One thing though, these posts are not supposed to be comprehensive: there is only information applicable to my team!

      @@ -2758,7 +2891,7 @@ I’ve started to use them for API related topics. Tools, terminology, and o - + Zally web UI with violations highlighting and links @@ -2823,8 +2956,13 @@ I’ve started to use them for API related topics. Tools, terminology, and o 2019-12-21T19:33:46Z Yuliya Bagriytag:www.aviskase.com,2019-12-21:/articles/2019/12/21/weekly-hmms-api-practices-yegor-bugaenko-culture/ - + + + API practices if you hate your customers +

      +

      A fun article from ACM Queue magazine about how to alienate +customers from using your API. I like an “anti-tutorial” form, +it is easier to recall bad patterns and behaviors.

      ]]> API practices if you hate your customers @@ -2904,8 +3042,11 @@ probably never consider how it affects existing canon.

      2019-12-14T22:32:58Z Yuliya Bagriytag:www.aviskase.com,2019-12-14:/articles/2019/12/14/weekly-hmms-advent-of-code-nginx-apis/ - + + + Advent of code +

      +

      Last week I forgot to mention that I started solving the Advent of Code challenges. This is my first year, so I don’t even try to have elegant solutions. Old trusty Python, lists’ and dicts’ galore.

      ]]> Advent of code @@ -2932,8 +3073,12 @@ The reason is simple: the goal is to go through all 25 days.]]> 2019-12-07T23:50:35Z Yuliya Bagriytag:www.aviskase.com,2019-12-07:/articles/2019/12/07/weekly-hmms-communities-laptops-linux/ - + This time, the proper title would be “Weekly Arghs.”

      +

      + + Testing communities +

      +

      When I decided to become a tester, the first-ever topic I encountered was discussions rants about “ISO/IEC/IEEE 29119 Software Testing” set of standards. I was fresh out of the university, so I couldn’t have legal access to these documents, and naturally, I sided with opponents. Good or bad (mostly bad), I don’t care: if you wish to have guidelines or standards for the profession, it must be freely accessible to everyone. The end.

      ]]>
      This time, the proper title would be “Weekly Arghs.”

      @@ -2967,7 +3112,7 @@ The reason is simple: the goal is to go through all 25 days.]]> - + Lenovo G560 keyboard @@ -2978,7 +3123,7 @@ The reason is simple: the goal is to go through all 25 days.]]> - + Dell XPS15 keyboard @@ -2998,10 +3143,16 @@ The reason is simple: the goal is to go through all 25 days.]]> 2019-11-30T19:13:56Z Yuliya Bagriytag:www.aviskase.com,2019-11-30:/articles/2019/11/30/weekly-hmms-watching-reading-learning/ - + + + Watching +

      +

      During lunch, I watch videos from conferences. This week my favorites were:

      + +

      The second one has a nice quotable passage:

      ]]> Watching @@ -3051,8 +3202,8 @@ A great API will empower you to be really lazy.]]> 2019-11-25T01:20:58Z Yuliya Bagriytag:www.aviskase.com,2019-11-25:/articles/2019/11/25/why-i-dont-use-postman/ - + Being an API person, many people would expect me to use Postman. It’s the most well-known tool for HTTP-based APIs and it’s so ubiquitous that some use it even for SOAP (not the best idea ever).

      +

      I did use Postman. My gist with installation scripts for Linux was so popular that Postman support team reached me out when it started causing non-obvious issues with updates. I also used this tool for internship courses. Yet I won’t do it anymore and let me explain why.

      ]]>
      Being an API person, many people would expect me to use Postman. It’s the most well-known tool for HTTP-based APIs and it’s so ubiquitous that some use it even for SOAP (not the best idea ever).

      I did use Postman. My gist with installation scripts for Linux was so popular that Postman support team reached me out when it started causing non-obvious issues with updates. I also used this tool for internship courses. Yet I won’t do it anymore and let me explain why.

      @@ -3106,8 +3257,12 @@ I did use Postman. My gist with installation scripts for Linux was so popular th 2019-11-23T02:45:30Z Yuliya Bagriytag:www.aviskase.com,2019-11-23:/articles/2019/11/23/weekly-hmms-fowler-collaborations-html-and-cts/ - + New week, new hmms!

      +

      + + Martin Fowler and exploratory testing +

      +

      All testers’ slack groups, forums, and blogs erupted this week with the tidings of joy: Martin Fowler wrote a post about exploratory testing. Of course, it seems a bit late and cursory, but at least now we have a very respected source to point to.

      ]]>
      New week, new hmms!

      @@ -3171,9 +3326,13 @@ I did use Postman. My gist with installation scripts for Linux was so popular th 2019-11-16T02:53:21Z Yuliya Bagriytag:www.aviskase.com,2019-11-16:/articles/2019/11/16/weekly-hmms-valuations-job-titles-conferences-apis/ - + To assume I could push anything on Fridays was too optimistic. Thus, I’ll commit to Saturdays.

      +

      Let’s start.

      +

      + + SaaS valuations +

      +

      My current company, NetGovern, operates with an open-book management mindset. Apart from other information, all employees know company finances so we can better understand what is our real position and where we plan to go next.

      ]]>
      To assume I could push anything on Fridays was too optimistic. Thus, I’ll commit to Saturdays.

      Let’s start.

      @@ -3223,7 +3382,11 @@ Let’s start. 2019-11-11T03:40:16Z Yuliya Bagriytag:www.aviskase.com,2019-11-11:/articles/2019/11/11/weekly-hmms-__init__/ - + + + __init__ +

      +

      This post initializes a series of weekly ponderings, interesting links, and other hmms. Think of it as typical a “Five for Friday,” but without number constraint and more emphasis on effects on my thought process. These posts should come out on Fridays, but because I forgot to commit, this one is late.

      ]]> __init__ @@ -3267,8 +3430,8 @@ Let’s start. 2019-10-26T16:52:40Z Yuliya Bagriytag:www.aviskase.com,2019-10-26:/articles/2019/10/26/a-testers-guide-on-hunting-for-api-related-sources/ - + You’ve got interested in APIs. Or you’re not a fan (yet) but you have to test it. Whatever the cause, you’d want to develop a mental model of this vast field. And a model construction demands a generous supply of information to consume and digest.

      +

      I prefer to seek knowledge in these five areas:

      ]]>
      You’ve got interested in APIs. Or you’re not a fan (yet) but you have to test it. Whatever the cause, you’d want to develop a mental model of this vast field. And a model construction demands a generous supply of information to consume and digest.

      I prefer to seek knowledge in these five areas:

        @@ -3366,9 +3529,14 @@ I prefer to seek knowledge in these five areas:]]> 2019-10-19T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-10-19:/articles/2019/10/19/how-to-test-api-usability-part-2/ - + This is part two of a two-parter. Check out part one.

        +

        + + Empirical methods +

        +

        The distinction between analytical and empirical methods is that the latter investigates how real users will use the product.

        +

        But don’t assume that empirical methods are by default better than analytical: both are important because they discover different problems. This research showed that heuristics were more efficient in finding documentation and structural problems, whereas empirical methods were more useful in finding UX and runtime specific issues.

        ]]>
        This is part two of a two-parter. Check out part one.

        @@ -3382,7 +3550,7 @@ But don’t assume that empirical methods are by default better than analyti - + Barchart with comparison of different issue types found via different methods @@ -3448,7 +3616,7 @@ But don’t assume that empirical methods are by default better than analyti - + Radar chart with comparison of developer expectations vs current state of API @@ -3465,7 +3633,7 @@ But don’t assume that empirical methods are by default better than analyti - + Venn diagram showing how different methods overlap in finding different issues @@ -3481,8 +3649,7 @@ But don’t assume that empirical methods are by default better than analyti 2019-10-13T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-10-13:/articles/2019/10/13/how-to-test-api-usability-part-1/ - + Disclaimer: this is a translation of the article written 2 years ago for a corporate blog. Bear in mind, at the moment of the writing I was testing SOAP services and Excel-based import/export at big government project, so most of the examples relate to that experience.

        ]]>
        Disclaimer: this is a translation of the article written 2 years ago for a corporate blog. Bear in mind, at the moment of the writing I was testing SOAP services and Excel-based import/export at big government project, so most of the examples relate to that experience.


        Usability is one of the most crucial quality attributes of an API. Let’s talk about why, when, and how we can assess this characteristic.

        @@ -3552,7 +3719,7 @@ There was a time when every new tester came to me during the onboarding and aske - + Cognitive walkthrough example @@ -3607,9 +3774,10 @@ There was a time when every new tester came to me during the onboarding and aske 2019-10-05T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-10-05:/articles/2019/10/05/internal-struggle-with-language-gymnastics/ - + During the RSTA class I asked a question about language and communications:

        +
        +

        On one hand, I wholly agree with the notion that we should be attentive with words. Like checking vs testing, “quality assurance”, and all those other things. I find myself as they say “nit-picking” quite often.

        +

        On the other hand, I also follow a rule “if your team understands you, words don’t matter”. Especially, if you come to the established project with existing vocabulary. It can be bizarre and absurd (my favorite was “bug validation” for the process of checking bug fixes), but it’s habitual to everyone, so you’ll likely spend more time trying to “reteach” than working. And you’ll probably fail.

        ]]>
        During the RSTA class I asked a question about language and communications:

        On one hand, I wholly agree with the notion that we should be attentive with words. Like checking vs testing, “quality assurance”, and all those other things. I find myself as they say “nit-picking” quite often.

        @@ -3667,7 +3835,7 @@ On the other hand, I also follow a rule “if your team understands you, wor 2019-09-30T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-09-30:/articles/2019/09/30/achievement-unlocked-rsta/ - + Landscape of testing related courses is problematic. On one hand, there are lots of courses. On the other hand, there are few courses I would consider. Either they are lackluster and certificate-centered, or entry-level-only, or mind-bogglingly expensive. And about the last bit. Many say that if a company provide a training budget, costs don’t matter. Well, maybe if that company is one of FAANG, you can think so. But I work in a small one and I don’t want to throw its (our?) money away.

        ]]>
        Landscape of testing related courses is problematic. On one hand, there are lots of courses. On the other hand, there are few courses I would consider. Either they are lackluster and certificate-centered, or entry-level-only, or mind-bogglingly expensive. And about the last bit. Many say that if a company provide a training budget, costs don’t matter. Well, maybe if that company is one of FAANG, you can think so. But I work in a small one and I don’t want to throw its (our?) money away.

        Nevertheless, there are always some courses and trainings which you just want to attend to. For example, those from Satisfice, Inc. That’s why I applied as soon as I saw time convenient online version of Rapid Software Testing Applied. And, BTW, the price was decent.

        I don’t want to write about the course agenda and curriculum: google is your friend. I will neither sing praises to its appeal and importance nor criticize its materials. We can argue as much as we like on should testers be able to code or not, but the knowledge of who James Bach and Michael Bolton are can be a mark of competency (necessary but not sufficient). And based on this knowledge, I think, it’s quite obvious what you can expect from such course. I also won’t describe what I learned: the most useful attainments are tacit and will surface in future work and articles.

        @@ -3696,8 +3864,8 @@ On the other hand, I also follow a rule “if your team understands you, wor 2019-09-24T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-09-24:/articles/2019/09/24/new-domain/ - + Yay, I’ve bought my own domain!

        +

        First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went with simple aviskase.com. Not that I am particularly invested into SEO and stuff, but all the articles recommend to be boring. Also, I used comparable products heuristic: most of the blogs I am subscribed to have the same dull TLD.

        ]]>
        Yay, I’ve bought my own domain!

        First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went with simple aviskase.com. Not that I am particularly invested into SEO and stuff, but all the articles recommend to be boring. Also, I used comparable products heuristic: most of the blogs I am subscribed to have the same dull TLD.

        BTW, if anyone ever was subscribed to RSS, sorry for whole new regeneration =(

        @@ -3710,7 +3878,7 @@ First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went w 2019-09-15T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-09-15:/articles/2019/09/15/soap-testing-101/ - + Disclaimer: this is a translation of the article written 2 years ago for a corporate blog. I didn’t do a word-for-word translation because the original article went through an editor, whose style was not that close to mine. Too watered down and “official.” Also, some examples don’t make sense in English. Still, I didn’t update it too radically. Bear in mind, at the moment of the writing I was testing SOAP services and Excel-based import/export at big government project, so most of the examples relate to that experience.

        ]]>
        Disclaimer: this is a translation of the article written 2 years ago for a corporate blog. I didn’t do a word-for-word translation because the original article went through an editor, whose style was not that close to mine. Too watered down and “official.” Also, some examples don’t make sense in English. Still, I didn’t update it too radically. Bear in mind, at the moment of the writing I was testing SOAP services and Excel-based import/export at big government project, so most of the examples relate to that experience.

        Sometimes you’ll see a block like that. It will contain my current thoughts on the subject or comments.

        @@ -3814,7 +3982,7 @@ First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went w - + XSD visualization in Oxygen @@ -3925,8 +4093,12 @@ First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went w 2019-09-08T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-09-08:/articles/2019/09/08/using-google-apps-scripts-for-productivity-improvements/ - + Google Apps Scripts are probably the most useful automation tools I’ve used. They can be used as “excel macros” for Sheets, form processing, and much more. Here I want to share three small scripts I made to improve productivity and task management.

        +

        + + Mark all emails as read +

        +

        If you ever got bothered by all archived and still unread emails in Gmail, this script can help you. It is based on the script by Mike Crittenden.

        ]]>
        Google Apps Scripts are probably the most useful automation tools I’ve used. They can be used as “excel macros” for Sheets, form processing, and much more. Here I want to share three small scripts I made to improve productivity and task management.

        @@ -3967,7 +4139,7 @@ First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went w 2019-09-04T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-09-04:/articles/2019/09/04/stereotype-rant/ - + We live in the world of memes and funny pictures. Nothing wrong with that. Some companies started posting humorous content too, and, again, that’s totally fine: I myself prefer these to dry corporate pitches. But it’s a real shame when you see a post like this from a test agency:

        ]]>
        We live in the world of memes and funny pictures. Nothing wrong with that. Some companies started posting humorous content too, and, again, that’s totally fine: I myself prefer these to dry corporate pitches. But it’s a real shame when you see a post like this from a test agency:

        What makes you feel more powerful:

        @@ -3994,7 +4166,7 @@ First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went w 2019-09-02T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-09-02:/articles/2019/09/02/your-api-is-your-public-image/ - +
        Disclaimer: this is a translation of the article written 2 years ago for a corporate blog. I didn’t do a word-for-word translation because the original article went through an editor, whose style was not that close to mine. Too watered down and “official.” Also, some examples don’t make sense in English. Still, I didn’t update it too radically. Bear in mind, at the moment of the writing I was testing SOAP services and Excel-based import/export at big government project, so most of the examples relate to that experience.

        ]]>
        Disclaimer: this is a translation of the article written 2 years ago for a corporate blog. I didn’t do a word-for-word translation because the original article went through an editor, whose style was not that close to mine. Too watered down and “official.” Also, some examples don’t make sense in English. Still, I didn’t update it too radically. Bear in mind, at the moment of the writing I was testing SOAP services and Excel-based import/export at big government project, so most of the examples relate to that experience.

        Sometimes you’ll see a block like that. It will contain my current thoughts on the subject or comments.

        @@ -4041,7 +4213,7 @@ First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went w - + API status table @@ -4071,7 +4243,7 @@ First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went w - + Unhelpful error message @@ -4107,8 +4279,11 @@ First I though about fancy-schmancy .me or .io. Or maybe aviska.se. But I went w 2019-09-17T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-08-26:/articles/2019/08/26/revision-testers-in-this-world/ - + Recently I’ve remembered my old article and wondered, how much my thoughts have changed and how they align to the principles of context-driven and modern testing (and yes, I don’t see them as contradicting each other).

        +
        +
        +

        Remark in the parentheses is a bit stupid. It was discussed in the AB Testing episode 94 and at least for Alan and Brent there is no real contradiction.

        ]]>
        Recently I’ve remembered my old article and wondered, how much my thoughts have changed and how they align to the principles of context-driven and modern testing (and yes, I don’t see them as contradicting each other).

        @@ -4140,7 +4315,7 @@ Remark in the parentheses is a bit stupid. It was discussed in the AB Testing ep 2019-08-08T23:51:52Z Yuliya Bagriytag:www.aviskase.com,2019-08-08:/articles/2019/08/08/lasha-tumbai-or-rm-rf-ru/ - + Just a quick note: I’ve switched to Pelican site generator, because ruby-shuby decided not to work. Unfortunately, switching and preserving multi-language support is cumbersome. So, I’ve decided to ditch RU version. For what it’s worth, I moved to Canada and no longer really interested in trying to “promote” myself in Russian speaking communities.

        ]]>
        Just a quick note: I’ve switched to Pelican site generator, because ruby-shuby decided not to work. Unfortunately, switching and preserving multi-language support is cumbersome. So, I’ve decided to ditch RU version. For what it’s worth, I moved to Canada and no longer really interested in trying to “promote” myself in Russian speaking communities.

        Yeah, yeah, “promote”, — written by a person who hasn’t write anything for ages. Right.

        Whoever read this blog in Russian, sorry. And, actually, I know that there were more traffic from CIS than from any other region >< But, two languages makes it too complicated and procrastinating to write in consistent manner (hysterical laugh).

        @@ -4154,8 +4329,8 @@ Remark in the parentheses is a bit stupid. It was discussed in the AB Testing ep 2019-07-30T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2019-07-30:/articles/2019/07/30/amazing-marvin-for-task-management/ - + I’m an infrequent blogger, so how weird it is that my last post was about RTM? I was a loyal RTM user for quite a time… Well, I have a new love now. His name is Marvin.

        +

        Marvin is still young, yet powerful. He has some problems and rough corners, but development couple (yes, just 2 persons!) is the most responsive and creative force I’ve seen. I’m still learning and tweaking my system there, but I want to describe the first working iteration to be able to improve and compare later.

        ]]>
        I’m an infrequent blogger, so how weird it is that my last post was about RTM? I was a loyal RTM user for quite a time… Well, I have a new love now. His name is Marvin.

        Marvin is still young, yet powerful. He has some problems and rough corners, but development couple (yes, just 2 persons!) is the most responsive and creative force I’ve seen. I’m still learning and tweaking my system there, but I want to describe the first working iteration to be able to improve and compare later.

        @@ -4552,8 +4727,8 @@ Marvin is still young, yet powerful. He has some problems and rough corners, but 2018-03-20T00:00:00Z Yuliya Bagriytag:www.aviskase.com,2018-03-20:/articles/2018/03/20/remember-the-milk-for-task-management/ - + Today I received a letter from Remember The Milk that I had won a free year of Pro. That’s great, yet I feel a bit sad because somehow everyone talks about every other todo app and not RTM.

        +

        I’ve been using RTM for several years. First, with free account, later with Pro. And it’s a second time I won a Pro :) RTM is great. For me, four killer features are:

        ]]>
        Today I received a letter from Remember The Milk that I had won a free year of Pro. That’s great, yet I feel a bit sad because somehow everyone talks about every other todo app and not RTM.

        I’ve been using RTM for several years. First, with free account, later with Pro. And it’s a second time I won a Pro :) RTM is great. For me, four killer features are: