Showing preview only (962K chars total). Download the full file or copy to clipboard to get everything.
Repository: pkardas/notes
Branch: master
Commit: 7b0d56be00b4
Files: 180
Total size: 904.1 KB
Directory structure:
gitextract_z_5iswqa/
├── .gitignore
├── README.md
├── books/
│ ├── architecture-hard-parts.md
│ ├── build.md
│ ├── clean-agile.md
│ ├── clean-code.md
│ ├── coaching-agile-teams.md
│ ├── code-complete.md
│ ├── comic-agile.md
│ ├── cracking-coding-interview/
│ │ ├── Dockerfile
│ │ ├── docker-compose.yml
│ │ ├── notes.md
│ │ ├── requirements.txt
│ │ └── src/
│ │ ├── ch01_arrays_and_strings/
│ │ │ ├── check_permutation.py
│ │ │ ├── is_unique.py
│ │ │ ├── one_away.py
│ │ │ ├── palindrome_permutation.py
│ │ │ ├── rotate_matrix.py
│ │ │ ├── string_compression.py
│ │ │ ├── string_rotation.py
│ │ │ ├── urlify.py
│ │ │ └── zero_matrix.py
│ │ └── ch02_linked_lists/
│ │ ├── delete_middle_node.py
│ │ ├── intersection.py
│ │ ├── linked_list.py
│ │ ├── loop_detection.py
│ │ ├── palindrome.py
│ │ ├── partition.py
│ │ ├── remove_dups.py
│ │ ├── return_kth_to_last.py
│ │ └── sum_lists.py
│ ├── ddd.md
│ ├── ddia.md
│ ├── docker-deep-dive.md
│ ├── elixir.md
│ ├── fundamentals-of-architecture.md
│ ├── go/
│ │ ├── ch01/
│ │ │ ├── Makefile
│ │ │ └── hello.go
│ │ ├── ch02/
│ │ │ ├── const.go
│ │ │ └── unicode.go
│ │ ├── ch03/
│ │ │ └── types.go
│ │ ├── ch04/
│ │ │ ├── case.go
│ │ │ ├── for.go
│ │ │ └── if.go
│ │ ├── ch05/
│ │ │ ├── anonymous.go
│ │ │ ├── deferExample.go
│ │ │ ├── functionAsParam.go
│ │ │ ├── functions.go
│ │ │ ├── functionsAreValues.go
│ │ │ └── returnFunction.go
│ │ ├── ch06/
│ │ │ └── pointers.go
│ │ ├── ch07/
│ │ │ ├── counter.go
│ │ │ ├── dependencyInjection.go
│ │ │ ├── embedding.go
│ │ │ ├── intTree.go
│ │ │ ├── interfaces.go
│ │ │ ├── iota.go
│ │ │ └── types.go
│ │ ├── ch08/
│ │ │ ├── customErrors.go
│ │ │ ├── errors.go
│ │ │ ├── panic.go
│ │ │ ├── recover.go
│ │ │ ├── sentinel.go
│ │ │ └── wrappingErrors.go
│ │ ├── ch09/
│ │ │ ├── formatter/
│ │ │ │ └── formatter.go
│ │ │ ├── main.go
│ │ │ └── math/
│ │ │ └── math.go
│ │ ├── ch10/
│ │ │ ├── deadlock.go
│ │ │ ├── deadlockSolution.go
│ │ │ └── goroutinesExample.go
│ │ └── notes.md
│ ├── hands-on-ml.md
│ ├── head-first-design-patterns/
│ │ ├── ch_01_strategy.py
│ │ ├── ch_02_observer.py
│ │ ├── ch_03_decorator.py
│ │ ├── ch_04_factory.py
│ │ ├── ch_05_singleton.py
│ │ ├── ch_06_command.py
│ │ ├── ch_07_adapter.py
│ │ ├── ch_07_facade.py
│ │ ├── ch_08_template_method.py
│ │ ├── ch_09_composite.py
│ │ ├── ch_09_iterator.py
│ │ ├── ch_10_state.py
│ │ ├── ch_11_virtual_proxy.py
│ │ └── notes.md
│ ├── kubernetes-book.md
│ ├── kubernetes-in-action.md
│ ├── nlp-book.md
│ ├── peopleware.md
│ ├── pragmatic-programmer.md
│ ├── pytest/
│ │ ├── .coveragerc
│ │ ├── Dockerfile
│ │ ├── docker-compose.yml
│ │ ├── notes.md
│ │ ├── requirements.txt
│ │ ├── setup.cfg
│ │ ├── src/
│ │ │ ├── __init__.py
│ │ │ ├── api.py
│ │ │ ├── cli.py
│ │ │ └── db.py
│ │ └── tests/
│ │ ├── ch_02/
│ │ │ ├── test_card.py
│ │ │ ├── test_classes.py
│ │ │ ├── test_exceptions.py
│ │ │ └── test_helper.py
│ │ ├── ch_03/
│ │ │ ├── conftest.py
│ │ │ ├── test_autouse.py
│ │ │ ├── test_count.py
│ │ │ ├── test_count_initial.py
│ │ │ ├── test_fixtures.py
│ │ │ ├── test_rename_fixture.py
│ │ │ └── test_some.py
│ │ ├── ch_04/
│ │ │ ├── conftest.py
│ │ │ ├── test_config.py
│ │ │ ├── test_tmp.py
│ │ │ └── test_version.py
│ │ ├── ch_05/
│ │ │ └── test_parametrize.py
│ │ ├── ch_06/
│ │ │ ├── pytest.ini
│ │ │ ├── test_builtin.py
│ │ │ ├── test_custom.py
│ │ │ └── text_combination.py
│ │ ├── ch_12/
│ │ │ ├── hello.py
│ │ │ └── test_hello.py
│ │ └── ch_15/
│ │ ├── conftest.py
│ │ ├── pytest.ini
│ │ └── test_slow.py
│ ├── python-architecture-patterns/
│ │ ├── Dockerfile
│ │ ├── Makefile
│ │ ├── docker-compose.yml
│ │ ├── notes.md
│ │ ├── requirements.txt
│ │ ├── setup.cfg
│ │ ├── src/
│ │ │ ├── __init__.py
│ │ │ ├── adapters/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── notifications.py
│ │ │ │ ├── orm.py
│ │ │ │ ├── redis_publisher.py
│ │ │ │ └── repository.py
│ │ │ ├── app.py
│ │ │ ├── bootstrap.py
│ │ │ ├── config.py
│ │ │ ├── domain/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── commands.py
│ │ │ │ ├── events.py
│ │ │ │ └── model.py
│ │ │ ├── redis_consumer.py
│ │ │ ├── service_layer/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── handlers.py
│ │ │ │ ├── message_bus.py
│ │ │ │ └── unit_of_work.py
│ │ │ └── views.py
│ │ └── tests/
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── e2e/
│ │ │ ├── __init__.py
│ │ │ ├── api_client.py
│ │ │ ├── redis_client.py
│ │ │ ├── test_app.py
│ │ │ └── test_external_events.py
│ │ ├── integration/
│ │ │ ├── __init__.py
│ │ │ ├── test_uow.py
│ │ │ └── test_views.py
│ │ └── unit/
│ │ ├── __init__.py
│ │ ├── test_batches.py
│ │ ├── test_handlers.py
│ │ └── test_product.py
│ ├── refactoring.md
│ ├── release-it.md
│ ├── system-design-interview.md
│ ├── tidy-first.md
│ └── understanding-distributed-systems.md
├── case-studies/
│ └── reddit.md
├── conferences/
│ ├── aws-innovate-ai-ml-21.md
│ ├── brown-bags.md
│ └── pycon-2022.md
├── courses/
│ └── fast-ai.md
├── patterns/
│ ├── abbreviations.md
│ └── architecture.md
└── teaching/
├── python-intermediate/
│ └── README.md
└── python-intro/
├── README.md
└── notebook.ipynb
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
.AppleDouble
.LSOverride
.idea
.ipynb_checkpoints
*/.pytest_cache/
git-user.sh
/excluded_resources/*
================================================
FILE: README.md
================================================
# 👉👉👉 Visit [musicat.fm](https://musicat.fm) 😻
You can connect Spotify and Apple Music to it to discover many cool statistics about your taste!
(I'm the author 🤩)
---
## Notes
### Books
👀 In progress:
- [System design interview](books/system-design-interview.md)
#### ✅ Finished:
- Code:
- [Clean Code: A Handbook of Agile Software Craftsmanship](books/clean-code.md)
- [Learning Go: An Idiomatic Approach to Real-World Go Programming](books/go/notes.md)
- [Python Testing with Pytest](books/pytest/notes.md)
- [Refactoring: Improving the Design of Existing Code](books/refactoring.md)
- [Tidy first?](books/tidy-first.md)
- Architecture:
- [Architecture Patterns with Python](books/python-architecture-patterns/notes.md)
- [Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems](books/ddia.md)
- [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](books/head-first-design-patterns/notes.md)
- [Release It! Design and Deploy Production-Ready Software](books/release-it.md)
- [Fundamentals of Software Architecture](books/fundamentals-of-architecture.md)
- Process:
- [Clean Agile: Back to Basics](books/clean-agile.md)
- [Domain-Driven Design: Tackling Complexity in the Heart of Software](books/ddd.md)
- [Peopleware: Productive Projects and Teams](books/peopleware.md)
- [The Pragmatic Programmer](books/pragmatic-programmer.md)
- [Comic Agilé](books/comic-agile.md)
- DevOps:
- [The Kubernetes Book](books/kubernetes-book.md)
- Product:
- :eyes:
- ML:
- [Speech and Language Processing: An Introduction to Natural Language Processing, Computational Linguistics and Speech Recognition](books/nlp-book.md)
#### ☑️ Finished partially:
- [Code Complete: A Practical Handbook of Software Construction](books/code-complete.md)
- [Cracking the Coding Interview](books/cracking-coding-interview/notes.md)
- [Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems](books/hands-on-ml.md)
- [Build](books/build.md)
- [Coaching Agile Teams](books/coaching-agile-teams.md)
#### ⏳ Queue:
- [Docker Deep Dive](books/docker-deep-dive.md)
- [Software Architecture: The Hard Parts](books/architecture-hard-parts.md)
- [Understanding Distributed Systems](books/understanding-distributed-systems.md)
- [Kubernetes in Action](books/kubernetes-in-action.md)
- [Elixir in Action](books/elixir.md)
### Case Studies
- [Reddit](case-studies/reddit.md)
### Conferences
- [PyCon 2022](conferences/pycon-2022.md)
- [AWS Innovate: AI/ML Edition 2021](conferences/aws-innovate-ai-ml-21.md)
- [Brown Bags](conferences/brown-bags.md)
### Patterns
- [Abbreviations](patterns/abbreviations.md)
- [Architecture](patterns/architecture.md)
### Teaching
- [Introduction to Programming: Python for beginners](teaching/python-intro)
- [Python Intermediate](teaching/python-intermediate)
### Courses
- [Course @ FastAI](courses/fast-ai.md)
================================================
FILE: books/architecture-hard-parts.md
================================================
[go back](https://github.com/pkardas/learning)
# Software Architecture: The Hard Parts: Modern Tradeoff Analysis for Distributed Architectures
Book by Pramod Sadalage, Neal Ford, Mark Richards, Zhamak Dehghani
================================================
FILE: books/build.md
================================================
[go back](https://github.com/pkardas/learning)
# Build
Book by Tony Fadell
- [1.1 Adulthood](#11-adulthood)
- [1.2 Get a job](#12-get-a-job)
- [1.3 Heroes](#13-heroes)
- [1.4 Don't (only) look down](#14-dont-only-look-down)
- [2.1 Just managing](#21-just-managing)
- [2.2 Data versus opinion](#22-data-versus-opinion)
- [2.3 Assholes](#23-assholes)
- [2.4 I quit](#24-i-quit)
- [3.1 Make the intangible tangible](#31-make-the-intangible-tangible)
- [3.2 Why storytelling](#32-why-storytelling)
- [3.3 Evolution versus disruption versus execution](#33-evolution-versus-disruption-versus-execution)
- [3.4 Your first adventure - and your second](#34-your-first-adventure---and-your-second)
## 1.1 Adulthood
When you are looking at the array of potential careers before you, the correct place to start is "What do I want to
learn?"
- NOT: How much money do I want to make?
- NOT: What title do I want to have?
- NOT: What company has enough name recognition?
Early adulthood is about watching your dreams go up in flames and learning as much as you can from the ashes.
Go where you can grow - people, mission, the opportunity are all that matters.
> The only failure is your twenties is inaction. The rest is trial and error.
Humans learn through productive struggle, by trying it themselves and screwing up and doing it differently next time.
You have to push yourself to the mountain, even if it means you might fall of a cliff.
## 1.2 Get a job
If you are going to throw your time, energy, and youth at a company, try to join one that's not just making a better
mousetrap. Find a business that's starting a revolution:
- it's creating a product that's wholly new or combines existing technology in a novel way that the competition can't
make or even understand
- this product solves a problem - a real pain point - that a lot of customers experience daily
- the novel technology can deliver on the company vision
- leadership is not dogmatic about what the solution looks like and is willing to adapt to their customers' needs
- it's thinking about a problem or a customer need in a way you've never heard before, but makes a perfect sense once
you hear it
Cool technology isn't enough, a great team isn't enough, plenty of funding isn't enough. You have to time you product
right. The world has to be ready to want it. If you're not solving a real problem, you can;t start a revolution.
Seemingly impossible problems that a decade ago would have cost billions to solve, requiring massive investments from
gian firms, can now be figured out with a smartphone app, a small sensor, and the internet.
If you are passionate about something - something that could be solving a huge problem one day - then stick with it.
Because one day, if you are truly solving a real issue, when the world is ready to want it, you will already be there.
You don't have to an executive right away, you don't have to get a job at the most amazing, world-changing company out
of college, but you should have a goal.
## 1.3 Heroes
The only thing that can make a job truly amazing or complete waste of time is the people.
You always have something to offer if you are curious and engaged. You can always trade and barter good ideas; you can
always be kind and find a way to help.
Try to get into a small company, the sweet spot is a business of 30-100 people building something worth building. You
could go to Google, Apple, Facebook, or some other giant company, but it will be hard to maneuver yourself to work
closely with the rock stars.
Smaller companies still have specialization, but usually without silos. And they have a different energy. The whole
company will be focused on working together to make one precious idea become reality. Anything unnecessary is shunned -
red tape and politics are typically nonexistent.
Being in that lifeboat with people you deeply respect is a joy. It is the best time you can have at work. It might be
the best time you can have.
## 1.4 Don't (only) look down
IC - individual contributor - a person who doesn't manage others. As an IC, you need to occasionally do 2 things:
- look up - look beyond the next deadline or project, bne sure the mission still makes sense to you and that the path to
reach it seems achievable
- look around - get out of your comfort zone and away from the immediate team you are on,talk to the other functions in
your company to understand their perspectives, needs, and concerns
Don't think doing the work just means locking yourself in a room - a huge part of it is walking with your team. The work
is reaching your destination together. Or finding a new destination and bringing your team with you.
## 2.1 Just managing
6 things you should know before becoming a manager:
- You don't have to be a manager to be successful - many people wrongly assume that the only path to more money and
stature is managing a team. There are alternatives that will enable you to get a similar paycheck.
- Remember that once you become a manager, you will stop doing the thing that made you successful in the first place -
your job will be communication, communication, communication, recruiting, hiring, firing, setting budgets, reviews,
one-to-one meetings, setting goals, keeping people on track, resolving conflicts, mentoring, ...
- Becoming a manager is a discipline - management is a learned skill, not a talent.
- Being exacting and expecting great work is not micromanagement - your job is to make sure the team produces
high-quality work, it only turns into micromanagement when you dictate the step-by-step process.
- Honesty is more important than style - you can be successful with any style as long as you never shy away from
respectfully telling people the uncomfortable, hard truth needs to be said.
- Don't worry that your team will outshine you - in fact, it's your goal, you should always be training someone on your
team to do your job, the better they are, the easier it is for you to move up and even start managing managers
When you are a manager, you are no longer just responsible for the work. You are responsible for human beings.
A star individual contributor is incredibly valuable. Valuable enough that many companies will pay them just as much as
they'd pay a manager. A truly great IC will be a leader in their chosen function and also become an informal cultural
leader, someone who people across the company will seek out for advice and mentorship.
Examining the product in detail and caring deeply about the quality of what your team is producing is not
micromanagement. That's exactly what you should be doing. Steve Jobs was bringing out a jeweler's loupe and looking at
individual pixels on a screen to make sure the user interface graphics were properly drawn.
As a manager, you should be focused on making sure the team is producing the best possible product.
It is very easy to turn 1:1s into a friendly chats that go nowhere, so clear meeting agenda can be beneficial.
If you are a manager - congrats, you're now a parent. Not because you should treat your employees like children, but
because it's now your responsibility to help them work through failure and find success.
## 2.2 Data versus opinion
Data driven decisions - you can acquire, study, and debate facts - relatively easy to make.
Opinion-driven - follow your gut and your vision - always hard and always questioned.
Make decisions, not everyone has to agree - it happens when one person has to make the final call. This isn't a
democracy, nor dictatorship - you can't give orders without explaining yourself.
Storytelling is how you get people to take a leap of faith to do something new. Creating a believable narrative that
everyone can latch on to is critical to moving forward and making hard choices. It's all that marketing comes down to.
You are selling - vision, guy, opinion.
> It's not data or intuition, it's data and intuition.
## 2.3 Assholes
Up to 12 percent of corporate senior leadership exhibit psychopathic traits. There are different assholes:
- Political assholes - people who master the art of corporate politics, but then do nothing but take credit for everyone
else's work. These assholes usually build a coalition of budding assholes around them
- Controlling assholes - micromanagers who systematically strangle the creativity and juy out of their team. They never
give people credit for their work, never praise it, and often steal it.
- Asshole assholes - they suck at work and everything else, mean jealous, insecure jerks. They cannot deliver, are
deeply unproductive, so they do everything possible to deflect attention away from themselves. They are generally out
of door pretty quickly.
- Mission-driven "assholes" - crazy passionate - they are neither easygoing nor easy to work with. Unlike true assholes,
they care.
Pushing for greatness doesn't make you an asshole. Not tolerating mediocrity doesn't make you na asshole. You need to
understand their motivations.
Controlling assholes won't listen. They will never admit they screwed up.
Things you can do when faced with a controlling asshole:
- kill'em with kindness
- ignore them
- try to get around them
- quit
Most people aren't assholes. And even if they are, they are also human. So don't walk into a job trying to get anyone
fired. Start with kindness. Try to make peace. Assume the best.
## 2.4 I quit
Sometimes you need to quit. Here is how you know:
- You are no longer passionate about the mission - every hour at your desk feels like an eternity
- You have tried everything - the company is letting you down
Once you do decide to quit, make sure you leave in the right way - try to finish as much as possible, find natural
breakpoint in your project.
Hating your job is never worth whatever raise, title, or perks they throw at you to stay.
The threat of leaving may be enough to push your company to get serious and make whatever change you are asking for. But
it might not. Quitting should not be a negotiating tactic - it should be the very last card you play.
Good things take time, big times take longer. If you flit from project to project, company to company, you will never
have the vital experience of starting and finishing something meaningful.
## 3.1 Make the intangible tangible
Don't just make a prototype of your product and think you're done. Prototype as much of the full customer experience as
possible.
Your product isn't only your product. It's the whole user experience. The customer journey and touchpoints:
- awareness (PR, search, social media, ads)
- education (website, email, blog, trial/demo)
- acquisition (partners, payment model)
- product (design, UX, performance)
- onboarding (quick guide, account creation, tips, how-to videos)
- usage (reliability, usability, updates, lifespan)
- support (troubleshooting, knowledge base, call center, community)
- loyalty (new product, newsletter, promotions, ratings/reviews)
## 3.2 Why storytelling
Every product should have a story, a narrative that explains why it needs to exist and how will it solve your customer's
problems. A good product story:
- it appeals to people's rational and emotional sides
- it takes complicated concepts and makes them simple
- it reminds people of the problem that's being solved - it focuses on the why
The story of your product, your company, and your vision should drive everything you do.
Virus of a doubt: "it is a way to get into people's heads, remind them about a daily frustration, get them annoyed about
it all over again. You get them angry about how it works now so they can get excited abut a new way of doing things."
Product's story is its design, features, images, videos, quotes from customers, tips from reviewers. The sum of what
people see and feel about this thing that you have created.
Why does this thing need to exist? Why does it matter? Why will people need it? Why will they love it? The longer you
work on something, the more the "what" takes over the "why". When you get wrapped in the "what", you get ahead of
people. You think everyone can see what you see. But they don't.
Earn their trust by showing that you really know your stuff or understand their needs. Of offer them something useful,
connect with them in a new way, so they feel assured that they're making the right choice with your company.
Appeal to their emotions, connect with something they care about. Their worries, their fears. Every person is different,
and everyone will read your story differently.
Analogies can be a useful tool in storytelling. They create a shorthand for complicated concepts.
## 3.3 Evolution versus disruption versus execution
Evolution - a small, incremental step to make something better
Disruption - a fork on the evolutionary tree - something fundamentally new that changes the status quo, usually by
taking a novel or revolutionary approach to an old problem
Execution - doing what you have promised to do and doing it well
Your version one product should be disruptive, not evolutionary. But disruption alone will not guarantee success.
Continue to evolve, but always seek out new ways to disrupt yourself.
Disruption should be important for you personally. If you've truly made something disruptive, your competition probably
won't be able to replicate it quickly.
Just don't overshoot. Don't try to disrupt everything at once.
As your disruptive product, process, or business model begins to gain steam with customers, your competitors will start
to get worried. They'll start paying attention, they will get pissed. When companies get angry they undercut your
pricing, try to embarrass you with marketing, use negative press, put in new agreements with sales to lock you out of
the business.
And they might sue you. If they can't innovate, they litigate. The good news is that a lawsuit means you've officially
arrived (you are a real threat, and they know it).
Disruptions - extremely delicate balancing act:
- you focus on making one amazing thing but forget that it has to be part of a single, fluid experience
- beautiful execution on everything else but the one thing that would have differentiated your product withers away
- you change too many things too fast and regular people can't recognize or understand what you have made, you can't
push people too far outside their mental model, not at first
Challenge yourself, over-deliver, create excellent solutions.
If you do it right, one disruption will fuel the next. One revolution will domino another.
## 3.4 Your first adventure - and your second
When releasing V1 you have the following tools to make decisions: Vision, Customer insights, Vision. Once you start
iterating on an existing product, you will have experience and data, so you can use your existing tools but in different
order: Data, Customer insights, Vision.
Locking yourself alone in a room to create a manifesto of your single, luminous vision looks and feels indistinguishable
from completely loosing your mind. Get at least one person - but preferably a small group - to bounce ideas off of.
Sketch your ideas together, then fulfill it together.
================================================
FILE: books/clean-agile.md
================================================
[go back](https://github.com/pkardas/learning)
# Clean Agile: Back to Basics
Book by Robert Cecil Martin
- [Chapter 1: Introduction to Agile](#chapter-1-introduction-to-agile)
- [Chapter 2: The Reasons For Agile](#chapter-2-the-reasons-for-agile)
- [Chapter 3: Business Practices](#chapter-3-business-practices)
- [Chapter 4: Team Practices](#chapter-4-team-practices)
- [Chapter 5: Technical Practices](#chapter-5-technical-practices)
- [Chapter 6: Becoming Agile](#chapter-6-becoming-agile)
- [Chapter 7: Craftsmanship](#chapter-7-craftsmanship)
- [Chapter 8: Conclusion](#chapter-8-conclusion)
- [Afterword](#afterword)
## Chapter 1: Introduction to Agile
The Agile Manifesto was written in February 2001 in Utah by 17 software experts. Once a movement become popular, the
name of that movement got blurred through misunderstanding and usurpation.
When did Agile begin? More than 50 000 years ago when humans first decided to collaborate on a common goal. The idea of
choosing small intermediate goals and measuring the progress after each is too intuitive, and too human, to be
considered any kind of revolution.
Agile was not the only game in town:
- Scientific Management - top-down, command-and-control approach. Big up-front planning followed by careful detailed
implementation. Worked best for projects that suffered a high cost of change and solved very well-defined problems
with extremely specific goals.
- Waterfall - logical descendant of Scientific Management. Even though it was not what the author was recommending, it
was the concept people took away from his paper. And it dominated the next 3 decades. It dominated but it didn't work.
How could thoroughly analyzing the problem, carefully designing a solution, and then implementing that design fail so
spectacularly over and over again.
The beginnings of the Agile reformation began in the late 1980s. In 1995 a famous paper on Scrum was written.
The Preamble of the Agile Manifesto:
> We are uncovering better ways of developing software by doing it and helping others do it.
The Agile Manifesto:
> **Individuals and interactions** over processes and tools.
> **Working software** over comprehensive documentation.
> **Customer collaboration** over contract negotiation.
> **Responding to change** over following a plan.
The Iron Cross of project management: good, fast, cheap, done - pick any three you like, you will not have the fourth.
A good manager drives a project to be good enough, fast enough, cheap enough and done as much as necessary. This is kind
of management that agile strives to enable.
Agile is a _framework_ that helps developers and managers execute this kind of pragmatic project management. However,
such management is not done automatic. It is entirely possible to work within Agile framework and still completely
mismanage the project and drive it to failure.
Agile provides data. An Agile development team produces just the kinds of data that managers need in order to make good
decisions:
- Velocity - how much the development team has gotten done every week.
- Burn-down chart - shows how many points remain until the next major milestone. Has a slope that predicts when the
milestone will probably be reached.
This data managers need to decide how to set the coefficients on the Iron Cross and drive the project to the best
possible outcome.
Agile development is first and foremost a feedback-driven approach. Each, week, each day, each hour, and even each
minute is driven by looking at the results of the previous week, day, hour and minute, and then making the appropriate
adjustments.
The Date (deadline) is usually fixed and is not going to change because some developers think they may not be able to
make it. At the same time, the requirements are wildly in flux and can never be frozen. This is because the customers
don't really know what they want. So the requirements are constantly being re-evaluated and re-thought.
The Waterfall model promised to give us a way to get our arms around this problem:
- The Analysis Phase - no real consensus on just what analysis is, the best definition: "it is what analyst do".
- The Design Phase - is where you split the project up into modules and design interfaces between those modules.
- The Implementation Phase - there is no way to successfully pretend it is done, meanwhile, the requirements are still
coming.
- The Death March Phase - customers are angry, stakeholders are angry, the pressure mounts, people quit. Hell.
It can be called - Runway Process Inflation - we are going to do the thing that did not work, and do it a lot more of
it.
Of course Waterfall was not an absolute disaster. It did not crush every software project into rubble. But it was, and
remains, a disastrous way to run a software project.
The Waterfall just makes so much sense. First, we analyze the problem, then we design the solution, and then we
implement the design. Simple. Direct. Obvious. And wrong.
An Agile project begins with analysis, but it is an analysis that never ends. Time before deadline is divided into
regular increments called _iterations_ or _sprints_. The size of an iteration is usually one or two weeks.
The first iteration (Iteration Zero). is used to generate a short list of features (stories). Iteration Zero is used to
set up development environment, estimate the stories and lay out the initial plan. This process of writing stories,
estimating them, planning them and designing never stops. Every iteration will have some analysis and design and
implementation in it.
In Agile project, we are always analyzing and estimating.
Software is not a reliably estimable process. We programmers simply do not know how long things will take. There is no
way to know how complicated a task is going to be until that task is engaged and finished.
After a couple of iterations we get insight how much time will be needed basing on past iterations. This number averages
at a relatively stable velocity. After four or five iterations, we will have a much better idea when this project will
be done.
We practice Agile in order to destroy hope before that hope can kill the project. Hope is the project killer. Hope is
what makes a software team mislead managers abut their true progress. Hope is a very bad way to manage a software
project. And Agile is a way to provide an early and continuous dose of cold, hard reality as a replacement for hope.
Some folks think that Agile is about going fast. It is not. Agile is about knowing, as early as possible, just how
screwed we are. The reason we want to know this as early as possible is so that we can manage the situation. Managers
manage software projects by gathering data and then making the best decisions they can base on that data.
Managers do this by making changes to the scope, the schedule, the staff, and the quality:
- Changing the Schedule - ask stakeholders if we can delay the project. Do this as early as possible.
- Adding Staff - in general, business is simply not willing to change the schedule. When new staff is added,
productivity plummets for a few weeks as the new people suck the life out of the old people. Then, hopefully, the new
people start to get smart enough to actually contribute. Of course, you need enough time, and enough improvement, to
make up for the initial loss.
- Decrease Quality - everyone knows that you can go much faster by producing crap. WRONG. There is no such thing as
quick and dirty. Anything dirty is slow. **The only way to go fast, is to go well**. If we want to shorten our
schedule, the only option is to _increase_ quality.
- Changing Scope - if the organization is rational, then the stakeholders eventually bow their heads in acceptance and
begin to scrutinize the plan.
Inevitably the stakeholders will find a feature that we have already implemented and then say "It is a real shame you
did that one, we sure do not need it". At the beginning of each iteration, ask the stakeholders which features to
implement first.
20 000 foot view of Agile:
> Agile is a process wherein a project is subdivided into iterations. The output of each iteration is measured and used
> to continuously evaluate the schedule. Features are implemented in the order of business value so that the most
> valuable things are implemented first. Quality is kept as high as possible. The schedule is primarily managed by
> manipulating scope.
## Chapter 2: The Reasons For Agile
Agile is important because of professionalism and the reasonable expectations from our customers.
- Professionalism - nowadays the cost of software failure is high, therefore we need to increase our professionalism. We
are surrounded by computers, and they all need to be programmed - they all need software. Nowadays, virtually nothing
of significance can be done without interacting with a software system. Now our actions are putting lives and fortunes
at stake.
- Reasonable Expectations - meeting expectations is one of primary goals of Agile development.
- we will not ship sh*t - Agile's emphasis on Testing, Refactoring, Simple Design and customer feedback is the
obvious remedy for shipping bad code.
- continuous technical readiness - system should be technically (solid enough to be deployed) deployable at the end
of every iteration.
- stable productivity - big redesigns are horrifically expensive and seldom are deployed. Developers instead, should
continuously keep the architecture, design and code as clean as possible, this allows to keep their productivity
high and prevent the otherwise inevitable spiral into low productivity and redesign.
- inexpensive adaptability - software - soft (easy to change), ware (product). Software was invented because we
wanted a way to quickly and easily change the behavior of our machines. Developers should celebrate change because
that is why we are here. Changing requirements is the name of the whole game. Our jobs depend on our ability to
accept and engineer changing requirements and to make those changes relatively inexpensive. If a change to the
requirements breaks your architecture, then your architecture sucks.
- continuous improvement - the older a software system is, the better it should be. Unfortunately it seldom happens.
We make things worse with time. The Agile practices of Pairing, TDD, Refactoring, and Simple Design strongly
support this expectation.
- fearless competence - people are afraid of changing bad code, you can break it, and if it breaks it will become
yours. This fear forces you to behave incompetently. Customers, users, and managers expect _fearless competence_.
They expect that if you see something wrong or dirty, you will fix it and clean it. They don't expect you to allow
problems to fester and grow - they expect you to stay on top of the code, keeping it as clean and clear as
possible. How to eliminate that fear? Use TDD.
- qa should find nothing - the Agile practices support this expectation.
- test automation - manual tests are always eventually lost. Manual tests are expensive and so are always a target
for reduction. Besides, asking humans to do what machines can do is expensive, inefficient, and immoral. Every
test that can be feasibly automated must be automated. Manual testing should be limited to those things that
cannot be automatically validated and to the creative discipline of Exploratory Testing.
- we cover for each other - each individual member of a software team makes sure that there is someone who can for
him if he goes down. It is your responsibility to make sure that one or more of your teammates can cover for you.
- honest estimates - you should provide estimates based on what you do and do not know. You can estimate in relative
terms (task B should take half of the time spent on task A), you can also estimate using ranges.
- you need to say "no" - when answer for something is "no", then the answer is really "no". For example if solution
for a problem can not be found.
- continuous aggressive learning - our industry changes quickly. We must be able to change with it. So learn, learn,
learn! Learn with or without company's help.
- mentoring - the best way to learn is to teach. So when new people join the team, teach them. Learn is to teach
other.
Customer Bill of Rights:
- You have the right to an overall plan and to know what can be accomplished when and at what cost.
- We cannot agree to deliver fixed scopes on gard dates. Either the scopes or the dates must be soft.
- You have the right to get the most possible value out of every iteration.
- The business has the right to expect that developers will work on the most important things at any given time, and
that each iteration will provide them the maximum possible usable business value.
- You have the right to see progress in a running system, proven to work by passing repeatable tests that you specify.
- You have the right to change your mind, to substitute functionality, and to change priorities without paying
exorbitant costs.
- You have the right to be informed of schedule and estimate changes, in time to choose how to reduce the scope to meet
a required date. You can cancel at any time and be left with a useful working system reflecting investment to date.
Developer Bill of Rights:
- You have the right to know what is needed with clear declarations of priority.
- Developers are entitled to precision in requirements and in the importance of those requirements. This right
applies within the context of an iteration. Outside an iteration, requirements and priorities will shift and
change.
- You have the right to produce hugh-quality work at all times.
- The business has no right to tell developers to cut corners or do low quality work. Or, to say this differently,
the business has no right to force developers to ruin their professional reputations or violate their professional
ethics.
- You have the right to ask for and receive help from peers, managers, and customers.
- This statement gives programmers the right to communicate.
- You have the right to make and update your estimates.
- You can change your estimate when new factors come to light. Estimates are guesses that get better with time.
Estimates are never commitments.
- You have the right to accept your responsibilities instead of having them assigned to you.
- Professionals accept work, they are not assigned work. A professional developer has every right to say "no" to a
particular job or task. It may be that the developer does not feel confident in their ability to complete the
task, or it may be that the developer believes the task better suited for someone else. Or, it may be that the
developer rejects the task for personal or moral reasons. Acceptance implies responsibility.
> Agile is a set of rights, expectations, and disciplines of the kind that form the basis of an ethical profession.
## Chapter 3: Business Practices
If you would like an accurate and precise estimate of a project, then break it down into individual lines of codes. The
time it takes you to do this will give you a very accurate and precise measure of how long it took you to build the
project.
Trivariate Analysis - such estimates are composed of three numbers: best-case, nominal-case, and worst-case. These
numbers are confidence numbers. The worst-case number is the amount of time which you feel 95% confident that the task
will be completed. The nominal-case has only 50% confidence, and the best case only 5%.
Stories and Points - a user story is an abbreviated description of a feature of the system, told from the point of view
of a user. We want to delay the specification of those details as long as possible, right up to the point where the
story is developed.
Story points are a unit of estimated effort, not real time. They are not even estimated time - they are estimated
effort. Velocity is not a commitment. The team is not making a promise to get 30 points done during the iteration. They
aren't even making the promise to try get 30 points done. This is nothing more than their best guess as to how many
points will be complete by the end of the iteration.
The Four-Quadrant Game (The Highest Return of Investment) - the stories that are valuable but cheap will be done right
away. Those that are valuable but expensive will be done later. Those that are neither valuable nor expensive might get
done one day. Those that are not valuable but are expensive will never be done.
Yesterday's weather - the best predictor of today's weather is yesterday's weather. The best predictor of the progress
of an iteration is the previous iteration.
The project is over when there are no more stories in the deck worth implementing.
User stories are simple statements that we use as reminders of features. We try not to record too much detail when we
write the story because we know that those details will likely change. Stories follow a simple set of guidelines that we
remember with the acronym INVEST:
- I - Independent - they do not need to be implemented in any particular order. This is a soft requirement because there
may be stories that depend on other stories. Still, we try to separate the stories so that there is little dependence.
- N - Negotiable - we want details to be negotiable between the developers and the business.
- V - Valuable - the story must have clear and quantifiable value to the business. Refactoring/Architecture/Code cleanup
is never a story. A story is always something that the business values.
- E - Estimable - must be concrete enough to allow the developers to estimate it.
- S - Small - a user story should be larger than one or two developers can implement in a single iteration.
- T - Testable - the business should be able to articulate tests that will prove that the story has been completed.
There are number of schemes for estimating stories:
- Flying Fingers
- Planning Poker
A spike is a meta-story, or a story for estimating a story. It is a spike because it often requires us to develop a long
but very thin slice through all the layers of the system. For example, there is a story you cannot estimate: Print PDF -
you have never used the PDF library. So you write a new story called Estimate Print PDF - now you estimate that story,
which is easier to estimate.
The goal of each iteration is to produce data by getting stories done. The team should focus on stories rather than
tasks within stories. It is far better to get 80% of the stories done than it is to get each story 80% done. Focus on
driving the stories to completion.
A story cannot be completed without the acceptance tests. If QA continues to miss the midpoint deadline, one iteration
after another, then the ratio of QA engineers to developers is likely wrong. After the midpoint, if all the acceptance
tests are done, QA should be working on the tests for the next iteration.
The definition of done is this: acceptance tests pass.
If we see a positive slope in velocity, it likely does not mean that the team is actually going faster. Rather, it
probably means that the project manager is putting pressure on the team to go faster. As that pressure builds, the team
will unconsciously shift the value of their estimates to make it appear that they are going faster. This is simple
inflation. The points are a currency, and the team is devaluing them under external pressure. The lesson is that
velocity is a measurement not an objective. Don't put pressure on the thing you are measuring.
Estimate is not a promise, and the team has not failed if the actual velocity is lower.
The practice of Small Releases suggest that a development team should release their software as often as possible. The
new goal, is Continuous Delivery - the practice of releasing the code to production after every change.
Acceptance Tests - Requirements should be specified by the business.
BDD - Behavior-Driven Development - the goal is to remove the techie jargon from the tests and make the tests appear
more like specifications that businesspeople would appreciate. At first, this was just another attempt at formalizing
the language of testing, in this case using 3 special adverbs: Given, When, and Then.
## Chapter 4: Team Practices
A metaphor can provide a vocabulary that allows the team to communicate effectively. On the other hand, some metaphors
are silly to the point of being offensive to the customer.
DDD solved the metaphor problem. Eric Evans coined the term _Ubiquitous Language_. What the team needs is a model of the
problem domain, which is described by a vocabulary that everyone (the programmers, QA, managers, customers, users)
agrees on.
The Ubiquitous Language is used in all parts of the project. It is a thread of consistency that interconnects the entire
project during every phase of its lifecycle.
A software project is not a marathon, not a sprint, nor a sequence of sprints. In order to win, you must pace yourself.
If you leap out of the blocks and run full speed, you will run out of energy long before you cross the finish line.
You must run at a Sustainable Pace. If you try to run faster than the pace you can sustain, you will have to slow down
and rest before you reach the finish line. Managers may ask you to run faster than you should. You must not comply. It
is your job to husband your resources to ensure that you endure the end.
> Working overtime is not a way to show your dedication to your employer. What it shows is that you are a bad planner,
> that you agree to deadlines to which you shouldn't agree, that you make promises you shouldn't make, that you are a
> manipulable laborer and not a professional. This is not to say that all overtime is bad, nor that you should never
> work overtime. There are extenuating circumstances for which the only option is to work overtime. But they should be
> extremely rare. And you must be aware that the cost of that overtime will likely be greater than the time you save on
> the schedule.
The most precious ingredient in the life of a programmer is sufficient sleep. Make sure you know how many hours of sleep
your body needs, and then prioritize those hours. Those hours will more than pay themselves back.
No one owns the code in an Agile project. The code is owned by the team as a whole. Any member of the team can check and
improve any module in the project at any time. The team owns the code collectively. Collective Ownership does not mean
that you cannot specialize. However, even as you specialize, you must also generalize. Divide your work between your
specialty and other areas of the code. Maintain your ability to work outside your specialty.
The continuous build should never break.
Standup Meeting:
- This meeting is optional. Many teams get by just fine without one.
- It can be less often than daily. Pick the schedule that makes sense to you.
- It should take ~10 minutes, even for large teams.
- This meeting follows a simple formula.
The basic idea is that the team members stand in a circle and answer 3 questions:
1. What did I do since the last meeting?
2. What will I do until the next meeting.
3. What is in my way?
4. [Optional] Whom do you want to thank?
No discussion. No Posturing. No deep explanations. No complaints. Everybody gets 30 seconds to answer those 3 questions.
## Chapter 5: Technical Practices
Without TDD, Refactoring, Simple Design and Pari Programming, Agile becomes an ineffective flaccid shell of what it was
intended to be.
TEST-DRIVEN DEVELOPMENT. Every required behavior should be entered twice: once as a test, and then again as production
code that makes the test pass.
The 3 rules of TDD:
1. Do not write any production code until you have first written a test that fails due to the lack of that code.
2. Do not write more of a test that is sufficient to fail - and failing to compile counts as a failure.
3. Do not write more production code that is sufficient to pass the currently failing test.
The tests are a form of documentation that describe the system being tested. This documentation is written in a language
that the programmers know fluently. It is utterly unambiguous, it is so formal it executes, and it cannot get out of
sync with the application code. The test are the perfect kind of documentation for programmers: code.
Remember that function that is hard to test after the fact? The function is hard to test because you did not design it
to be easy to test. You wrote the code first, and you are now writing the tests as and afterthought. By writing the
tests first, you will decouple the system in ways that you had never thought about before. The whole system will be
testable, therefore, the whole system will be decoupled.
REFACTORING. Refactoring is the practice of improving the structure of the code without altering the behavior, as
defined by tests. In other words, we make changes to the names, the classes, the functions and the expressions without
breaking any of the tests.
Red/Green/Refactor:
1. We create a test that fails.
2. Then we make the test pass.
3. Then we clean up the code.
4. Return to step 1.
The word Refactoring should never appear on a schedule. Refactoring is not the kind of activity that appears on a plan.
We do not reserve time for refactoring. Refactoring is simply part of our minute-by-minute, hour-by-hour approach to
writing software.
Sometimes the requirements change is such a way that you realize the current design and architecture of the system is
suboptimal, and you need to make a significant change to the structure of the system. Such changes are made within the
Red/Green/Refactor cycle. We do not create a project specifically to change the design. We do not reserve time in the
schedule for such large refactorings. Instead, we migrate the code one small step at a time, while continuing to add new
features during normal Agile cycle.
SIMPLE DESIGN. The practice of Simple Design is one of the goals of Refactoring. Simple Design is the practice of
writing only the code that is required with a structure that keeps it simplest, smallest, and the most expressive.
Rules of Simple Design:
1. Pass all the tests.
2. Reveal the intent - It should be easy to read and self-descriptive. This is where we apply many of the simpled and
more cosmetic refactorings. We also split large functions into smaller, better-named functions.
3. Remove duplication.
4. Decrease elements - Once we have removed all the duplication, we should strive to decrease the number of structural
elements, such as classes, functions, variables.
The more complex the design, the greater the cognitive load placed on the programmers. That cognitive load is Design
Weight. The greater the weight of that design, the more time and effort are required for the programmers to understand
and manipulate the system.
PAIR PROGRAMMING. Pairing is the act of two people working together on a single programming problem. Any configuration
is fine (the same workspace, sharing the screen, keyboard, ping-pong, ...). We pair so that we behave like a team. When
a member of a team goes down, the other team members cover the hole left by that member and keep making progress towards
the goal. **Pairing is the best way, by far, to share knowledge between team members and prevent knowledge silos from
forming. It is the best way to make sure that nobody on the team is indispensable.**
The word "pair" implies that there are just 2 programmers involved in a pairing session. While this is typically true,
it is not a rule.
Generally, managers are pleased to see programmers collaborating and working together. It creates the impression that
work is being done.
**Never, ever, ever, ask for permission to pair. Or test. Or refactor. Or... You are the expert. You decide.**
## Chapter 6: Becoming Agile
Agile Values:
1. Courage - It is reckless to conform to a schedule by sacrificing quality. The belief that quality and discipline
increase speed is a courageous belief because will constantly be challenged by powerful but naive folks who are in a
hurry.
2. Communication - A team that sits together and communicates frequently can work miracles. We value direct and frequent
communication that crosses channels. Face-to-face, informal, interpersonal conversations.
3. Feedback - Maximize the frequency and quantity of feedback. They allow us to determine when things are going wrong
early enough to correct them. They provide massive education about the consequences of earlier decisions.
4. Simplicity - Numbers of problems should be reduced to minimum. Therefore, indirection can be kept to a minimum.
Solutions can be simple. This applies to the software, but it also applies to the team. Passive aggression is
indirection. Keep the code simple. Keep the team simpler.
These values are diametrically opposed to the values of large organisations who have invested heavily in
middle-management structures that value safety, consistency, command-and-control, and plan execution.
It is not really possible to transform such an organisation to Agile.
Agile coaches are members of the team whose role is to defend the process within the team. In the heat of development,
developers may be tempted to go off process. Perhaps they inadvertently stop pairing, stop refactoring, or ignore
failures in the continuous build. The coach acts as the team's conscience, always reminding the team of the promises
they made to themselves and the values they agreed to hold. This role typically rotates from one team member to the next
on an informal schedule and based on need. A mature team working steadily along does not require a coach. On the other
hand, a team under some kind of stress (schedule, business or interpersonal) may decide to ask someone to fill the role
temporarily.
Every member of an Agile team needs to understand the values and techniques of Agile. Therefore, if one member of the
team is trained, all members of the team should be trained.
Agile is for small- to medium-sized teams. period. It works well for such teams. Agile was never intended for large
teams. The problem of large teams is a problem societies and civilizations. And large teams are a solved problems.
Agile was invented because we did not know how to effectively organize a relatively small group of programmers to be
effective. Software development needed its own process because software is really like nothing else.
The answer to the question of Agile in the large is simply to organize your developers into small Agile teams, then use
standard management and operations research techniques to manage those teams.
Great tools do the following:
- Help people accomplish their objectives
- Can be learned "well enough" quickly
- Become transparent to users
- Allow adaptation and exaptation
- Are affordable
Git is an example of a great tool.
Your team should establish the pattern of work compatible with their specific context first, and then consider using
tools that support their workflow. Workers use and control tools, tools don't control and use people. You don't want to
get locked into other people's process flows.
ALM - Agile Lifecycle Management systems despite being feature rich and commercially successful, ALM tools utterly fail
at being great.:
- ALMs tend to be complicated, usually demanding up-front training.
- These tools often require constant attention.
- ALM tools aren't always easily adapted.
- ALM tools can be expensive.
- ALM does rarely work the way your team does, and often their default mode is at odds with Agile methods. For example
many ALM tools assume that team members have individual work assignments, which makes them nearly unusable for teams
who work together in a cross-functional way.
You can try different forms of Agile practices and check which one is the most relevant to your team's needs:
- Kanban - making the work visible, limiting work in progress and pulling work through the system.
- Scrum and XP - short daily meetings, a product owner, a process facilitator (Scrum Master), retrospectives, a
cross-functional team, user stories, small releases, refactoring, writing tests first, and pair programming.
- Align team events - when the team events across multiple teams (standups, retrospectives) are aligned in time, it is
possible to then roll up daily and systematic impediments via an escalation tree.
- Escalation trees - if it makes sense to always work on items that produce the highest value, then it makes sense to
escalate impediments immediately via a well-defined escalation path.
- Regular interteam interaction - regular interaction between the Scrum Masters, Product Owners and team members who are
working together toward a common deliverable.
- Portfolio Kanban - sets work in progress limits at the initiative level in order to ensure that the organization is
focused on the highest-value work at all times.
- Minimum Viable Increments - what is the shortest path to producing the highest value in the shortest time. A growing
number of organizations are taking this to extreme by implementing Continuous Delivery - releasing small updates on a
frequent basis, sometimes as frequently as multiple times per day.
Enablers of multiteam coordination:
- SOLID - especially useful for simplifying multiteam coordination by dramatically reducing dependencies.
- Small, valuable user stories - limit the scope of dependencies, which simplifies multiteam coordination.
- Small, frequent releases - whether these releases are delivered to the customer or not, the practice of having a
releasable product across all the teams involved helps to surface coordination and architectural issues so that the
root cause can be found and addressed.
- Continuous Integration - calling for integration across the entire product after every checkin.
- Simple Design - one of the hardest practices to learn and apply because it is one of the most counter-intuitive
practices. When coordinating the work of massive dependencies between teams, monolithic, centralized, preplanned
architectures create massive dependencies between teams that tend to force them to work in lock step, thus defeating
much of the promise of Agile. Simple Design, especially when used with practices such as a microservices architecture,
enables Agility in large.
## Chapter 7: Craftsmanship
Many companies misunderstood Agile. Managers are willing to push developers to work faster and are using the full
transparency of the process to micromanage them. Developers are pushed hard to fit their estimates into the imposed
milestones. Failing to deliver all story points in a sprint means developer must work harder in the next sprint to make
up the delay. If the product owner thinks developers are spending too much time on things like automated tests,
refactoring, or pairing they simply tell them to stop doing it.
Strategic technical work has no place in _their_ Agile process. There is no need for architecture or design. The order
is to simply focus on the highest-priority item in the backlog and get it done as fast as possible. This approach
results in a long sequence of iterative tactical work and accumulation of technical debt. Bugs are accumulating,
delivery time goes up, people start to blame one another.
> Companies are still not mature enough to understand that technical problems are in fact business problems.
A group of developers met in November 2008 in Chicago to create a new movement: Software Craftsmanship.
Manifesto:
As aspiring Software Craftsmen, we are raising the bar or professional software development by practicing it and helping
others learn the craft. Through this work we have come to value:
- Not only working software, but also well-crafted software.
- Not only responding to change, but also steadily adding value.
- Not only individuals and interactions, but also a community of professionals.
- Not only customer collaboration, but also productive partnership.
The Software Craftsmanship manifesto describes an ideology, a mindset. It promotes professionalism through different
perspectives.
**Well-crafted software** - code that is well-designed and well tested. It is code that we are not scared to change and
code that enables business to react fast. It is code that is both flexible and robust.
**Steadily adding value** - no matter what we do, we should always be committed to continuously provide increasing value
to our clients and customers.
**A community of professionals** - we are expected to share and learn with each other, raising the bar of our industry.
We are responsible for preparing the next generation of developers.
**Productive partnership** - we will have a professional relationship with our clients and employers. We will always
behave ethically and respectfully, advising and working with our clients and employers in the best way possible. We will
expect a relationship of mutual respect and professionalism.
We will look at our work not as something we need to do as part of a job but as a professional service we provide. We
will take ownership of our own careers, investing our own time and money to get better at what we do. Craftspeople
strive to do the best job they can, not because someone is paying, but based on a desire to do things well.
Developers should not ask for authorization for writing tests. They should not have separate tasks for unit testing or
refactoring. These technical activities should be factored into the development of any feature. They are not optional.
Managers and developers should only discuss what is going to be delivered and when, not how. Every time developers
volunteer details of how they work, they are inviting managers to micromanage them. Developers should be able to clearly
describe how they work and the advantages of working that way to whomever is interested. What developers should not do
is to let other people decide how they work.
Conversations between developers and business should be about why, what and when - not how.
Craftsmanship promotes software development as a profession. A profession is part of who we are. A job is a thing that
we do but is not part of who we are. A profession is something we invest in. It is something we want to get better at.
We want to gain more skills and have a long-lasting and fulfilling career.
Combining Agile and Craftsmanship is the perfect way to achieve business agility.
## Chapter 8: Conclusion
This book covered basics of Agile.
## Afterword
Ask the developers in an "Agile organization" what Agile is, and you will likely get a very different answer than if you
ask anyone beyond the level of a software development manager.
Developers understand Agile to be a methodology for streamlining the development process and for making software
development more predictable, more practicable, and more manageable.
Many developers are blissfully unaware of management's use of the metrics provided by the implementation of Agile
practices and the data it produces.
================================================
FILE: books/clean-code.md
================================================
[go back](https://github.com/pkardas/learning)
# Clean Code: A Handbook of Agile Software Craftsmanship
Book by Robert Cecil Martin
- [Chapter 1: Clean Code](#chapter-1-clean-code)
- [Chapter 2: Meaningful names](#chapter-2-meaningful-names)
- [Chapter 3: Functions](#chapter-3-functions)
- [Chapter 4: Comments](#chapter-4-comments)
- [Chapter 5: Formatting](#chapter-5-formatting)
- [Chapter 6: Objects and Data Structures](#chapter-6-objects-and-data-structures)
- [Chapter 7: Error Handling](#chapter-7-error-handling)
- [Chapter 8: Boundaries](#chapter-8-boundaries)
- [Chapter 9: Unit Tests](#chapter-9-unit-tests)
- [Chapter 10: Classes](#chapter-10-classes)
- [Chapter 11: Systems](#chapter-11-systems)
- [Chapter 12: Emergence](#chapter-12-emergence)
- [Chapter 13: Concurrency](#chapter-13-concurrency)
- [Chapter 17: Smells and Heuristics](#chapter-17-smells-and-heuristics)
## Chapter 1: Clean Code
- ugly code is expensive
- take your time to write a good code
- bad code programmer's fault, not PO's, manager's or anyone's else
- bad code is like a building with broken windows - people see ugly building and stop caring
- code like a prose, code should look like you care
- make the language look like it was made of the problem
- code rot quickly
## Chapter 2: Meaningful names
Variable name should answer all the questions. It should tell why it exists. If a name requires a comment it does not
reveal its content. Names should be pronounceable. One letter variables are hard to `grep` in the code - should be ONLY
as local variables inside short methods. The length of a name should correspond to the size of its scope. Avoid
encodings.
> Difference between a smart programmer and a professional programmer is that professional programmer understands that
> **clarity is a king**.
Don't be funny 😔 People tend to forget jokes, so people will forget true meaning of a variable. Choose clarity over
entertainment. Do not use slang or culture-dependant names.
Pick one word per concept, e.g. `get` instead of `fetch`, `retrieve`, ...
## Chapter 3: Functions
Functions are the first line of organisation in any program. Functions should be small. No more than 2-3 indents.
> Functions should do one thing. They should do it well. They should do it only.
The reason we write functions is to decompose a larger concept. A function should not mix the levels of abstractions.
> You know you are working on clean code when each routine turns out to be pretty much what you expected.
Don't be afraid to make a name long. The more function arguments the worse - difficulties with testing.
Passing a boolean flag to a function is extremely ugly. Grouping arguments into objects seems like cheating, but it is
not.
Functions should have no side effects
*Command Query Separation* - functions should either do something or answer something, but not both.
Exceptions are preferred than error codes. Suggestion to extract exception handling to separate function.
*Don't repeat yourself* - duplication may be the root of all evil in software. Database norms formed to eliminate
duplication in data, OOP concentrates the code, etc.
> Writing software is like any other kind of writing. When you write a paper or article, you get your thoughts down
> first, then you massage it until it **reads well**.
> The art of programming is, and always has been, the art of language design.
## Chapter 4: Comments
Comments are usually bad, they mean you failed to express yourself in code. IMO: the Best comments are the ones that are
explaining why things were done in a particular way.
Don't put historical discussions or irrelevant details into the comments.
## Chapter 5: Formatting
Code formatting is important. Visual design of the code is important. Variable should be declared "in well-known for
everybody places". Functions should show natural flow -> top-down.
Another matter is alignment, e.g. of test cases in parametrised tests. However, variables declarations is an overkill.
However, a team should agree upon a single formatting style.
## Chapter 6: Objects and Data Structures
Hiding implementation is about abstractions.
The Law of Demeter - a module should not know about the innards of the objects it manipulates. Class *C* has a method *
f*, method *f* should call the methods of: *C*, object created by *f*, object passed as an argument to *f* or object
held in an instance variable of *C*.
Train wreck: `ctxt.getOptions().getScratchDir().getAbsolutePath()` - a bunch of couples train cars. Does it violate The
Law of Demeter? `ctxt` contains options, which contain a scratch directory, which has absolute path - a lot of
knowledge. However, in this case this law does nto apply because these are data structures with no behaviour. It would
be good to hide the structure of `ctxt`, e.g.: `ctxt.getScratchDirectoryOption().getAbsolutePath()`.
Data Transfer Objects - a class with public variables and no functions, e.g. for communicating with the database.
Objects - expose behaviour and hide data, data structures - expose data and have no significant behaviour.
## Chapter 7: Error Handling
Error handling is important, but if it obscures logic, it is wrong. Exceptions are preferred over return codes - return
codes can clutter the caller with unnecessary code.
`try` blocks are like transactions, `catch` has to leave the program into a consistent state.
Error messages need to be informative - mention the operation that failed and the type of failure.
It might be a good idea to wrap library's error with your own exceptions - this makes library easily replaceable.
## Chapter 8: Boundaries
How to keep boundaries of our system clean - e.g. when using external libraries:
- when working with collections, wrap them with object and provide only required functionalities.
- write learning tests - write tests to explore and understand API
- our code shouldn't know too many details about 3rd-party library
- use ADAPTER interface - converted from our perfect interface to the provided interface
## Chapter 9: Unit Tests
The Three Laws of TDD:
- You may not write production code until you have written a failing unit test
- You may not write more of unit code than is sufficient to fail, and not compiling is failing
- You may not write more production code than is sufficient to pass the currently failing test
Test code is just as important as production code. It is not second-class citizen. It must be kept as clean as
production code.
The Build-Operate-Check pattern - each test is split into three parts:
1. build up the test data
2. operate on test data
3. check that the operation yielded the expected results
Test code must be: simple, succinct, expressive, however it doesn't need to be as efficient as production code.
One test should test a single concept.
Clean tests follow 5 rules - FIRST:
- F - Fast - tests should be fast, they should run quickly, if they don't you won't want to run them frequently.
- I - Independent - Tests should not depend on each other, one test should not set up conditions for the next test
- R - Repeatable - Tests should be repeatable in any environment (office, home, train without network), if they are not
you will have an excuse for why they fail
- S - Self-Validating - Tests should not have a boolean output, they should either fail or pass
- T - Timely - Tests need to be written in a timely fashion, should be written just before the production code
## Chapter 10: Classes
Classes should be small. The second rule is that they should be smaller than that. Naming is most probably the best way
of determining class size. If we cannot derive a concise name for a class, then it is likely too large.
The Single Responsibility Principle - a class or module should have one, and only one reason to change.
Cohesion - classes should have a small number of instance variables. Each of class' methods should manipulate one or
more of those variables.
Open-Closed Principle - class should be open for extensions but closed for modifications.
Dependency Inversion Principle - our classes should depend upon abstractions, not on concrete details.
## Chapter 11: Systems
It is a myth we can get the systems "right the first time". Instead, we should implement only today's stories, then
refactor and expand the system to implement new stories tomorrow. This is the essence of iterative and incremental
agility.
Use the simplest thing that can possibly work.
## Chapter 12: Emergence
According to Kent, a design is simple if it follows these rules:
- runs all tests - system needs to be testable - if this can not be achieved, system should not be released, all tests
need to pass
- contains no duplication
- expresses the intent of the programmer - the clearer the code, the less time others will have to spend understanding
it (small functions and classes, good names)
- minimises the number of classes and methods - the least important rule, above rules are more important, however
overall goal should be to keep system small
Can set of practices replace experience? No. On the other hand, practices are a crystallised form of the many decades of
experience of many authors.
## Chapter 13: Concurrency
Concurrency is a decoupling strategy. It helps to decouple what gets done from when it gets done. In single-threaded
apps wheat and when are strongly coupled.
Concurrency Defence Principles:
- Single Responsibility Principle - concurrency-related code should be kept separately from other code
- limit the access to any data that may be shared
- a good way of avoiding shared data is to avoid sharing data in the first place
- use copy of data , collect results from multiple threads and merge results
- threads should be as independent as possible
Java supports thread-safe collections, e.g. ConcurrentHashMap, there are other classes to support advanced concurrency:
ReentrantLock - a lock that can be acquired and released, Semaphore - a classic lock with count, CountDownLatch - a lock
that waits for a number of events before releasing all threads waiting on it.
Couple of behaviours:
- Bound Resources - resources of a fixed size or number used in a concurrent environment, e.g. database connection
- Mutual Exclusion - only one thread can access shared data or a shared resource at a time
- Starvation - thread(s) prohibited from proceeding for an excessively long time or forever
- Deadlock - two or more threads waiting for each other to finish
- Livelock - threads in lockstep, each trying to do work but finding another "in the way", threads continue trying to
make progress but are unable
Execution models:
- producer - consumer - one or more threads create some work and place it in a queue, one or more consumer threads
acquire that work from queue and complete it
- readers - writers - writers wait until there is no readers before allowing the writer to perform an update, if there
are continuous readers, writers will starve
- dining philosophers - a hungry philosopher needs 2 forks before accessing the food, after consumption releases forks
and waits until he is hungry again. There are number of solutions to this problem.
`synchronised` keyword introduces a lock in Java. Locks are expensive so use them carefully, also such sections should
be small.
Graceful shutdown is hard to get correct. Think about it early and get it working early.
General tips:
- get your non-threaded code working first
- make threaded-based code pluggable (one thread, n threads, ...)
- run with more threads than processors
## Chapter 17: Smells and Heuristics
Comments:
- Metadata should not appear in the comment (author, modification date). Comments should be reserved for technical notes
only.
- Do not write comments that will become obsolete.
- Do not paraphrase code.
- Be brief and correct.
- Instead of commenting-out code - delete it.
Environment:
- You should be able to check out system with one simple command.
- You should be able to run all unit tests with just one command.
Functions:
- Functions should have a small number of arguments, no argument is best. More than 3 arguments is very questionable and
should be avoided.
- Output arguments are counterintuitive readers expect arguments to be inputs, not outputs. If function must change
state of something, have it change the state of the object it is called on.
- Flag arguments should be avoided (boolean flags) - they loudly declare function is doing multiple things.
- Methods that are never called should be removed. Dead code is wasteful.
General:
- The ideal source files should contain one, and only one language (for example Java + JavaScript snippets + English
comments).
- Function / Class should implement the behaviours that another programmer could reasonably expect.
- Check every boundary condition.
- No duplication, perhaps the most important rule. Duplicated code means a missed opportunity for abstraction. Codd
Normals Forms are a strategy for eliminating duplication.
- It is important to create abstractions that separate higher level general concepts from lower level detailed concepts.
- High level concepts should be independent of low level derivatives.
- A well-defined interface does not offer very many functions to depend upon, so coupling is low. Good software
engineers learn to limit what they expose at the interfaces of their classes and modules.
- Get rid of dead code - code that is never executed.
- Variables and functions should be defined close to where they are used.
- Use consistent naming.
- Keep source code organised and free of clutter.
- Things that don't depend upon each other should not be artificially coupled.
- Feature envy - the methods of a class should be interested in the variables and functions of the class they belong to,
and not the variables and functions of other classes.
- Code should be expressive as possible.
- Code should be placed where a reader would naturally expect it to be (the principle of the least surprise).
- Think if function should be static or not.
- Variables should have meaningful names, also use intermediate variables when performing difficult calculations.
- Function names should say what they do, if you can't understand what function does by reading the call - change the
name.
- Polymorphism is preferred over if / else or switch / case statements.
- Follow code standards.
- Replace magic numbers with named constants.
- Be precise, use appropriate data structures.
- Encapsulate conditions - boolean logic is hard to understand without having to see it in the context, extract the
functions that explain the intent of the conditional.
- Avoid negative conditions - harder to understand.
- Functions should do one thing.
- Encapsulate boundary conditions.
- The statements within a function should all be written at the same level of abstraction.
- Keep configurable data at high levels.
- Law of Demeter - we don't want a single module to know much about its collaborators.
Names:
- Choose descriptive names. Names in software are 90% of what makes software readable.
- Choose names at the appropriate level of abstraction. Don't pick names that communicate implementation details.
- Use standard nomenclature where possible.
- Use unambiguous names.
- Names should describe side effects.
Tests:
- Use coverage tool.
- Don't skip trivial tests.
- Test boundary conditions.
- Tests should be fast.
================================================
FILE: books/coaching-agile-teams.md
================================================
[go back](https://github.com/pkardas/learning)
# Coaching Agile Teams
Book by Lyssa Adkins
- [1. Will I be a Good Coach?](#1-will-i-be-a-good-coach)
## 1. Will I be a Good Coach?
If teams are to have kinds of stellar experiences, leverage agile to teh full competitive advantage it was meant to
provide.
Agile coaching matters because it helps both, producing products that matter in the real, complex and uncertain world,
and adding meaning to people's work lives.
Agile is easy to get going yet hard to do well.
Imagine a team that admits mistakes, reinforces their shared values, forgives one another, and moves on. Do you think
such a team would come up with astonishing ideas?
An agile (or Scrum) coach is:
- someone who appreciates teh depths of agile practices and principles and can help teams appreciate them too
- someone who has faces big dragons, organizational impediments, and has become a coach to managers and other outsiders
in the course of addressing them
- someone who can help management at all levels of the organization the benefits of working agile
- someone who has brought ideas from professional facilitation, coaching, conflict management, mediation, theater and
more to help the team become a high-performance team
Native wiring for coaching:
- ability to "read a room", ability to read emotion in the air and know whether all is good
- care about people more than products
- cultivate curiosity
- believe that people are basically good
- they know that plans fall apart, so they act in the moment with the team
- any group of people can do good things
- it drives them crazy when someone says "yeah, I know, it's a waste of time, but that's how we do it here"
- chaos and destruction are simply building blocks for something better
- they risk being wrong
================================================
FILE: books/code-complete.md
================================================
[go back](https://github.com/pkardas/learning)
# Code Complete: A Practical Handbook of Software Construction
Book by Steve McConnell
- [Chapter 1: Software Construction](#chapter-1-software-construction)
- [Chapter 2: Metaphors for a Richer Understanding of Software Development](#chapter-2-metaphors-for-a-richer-understanding-of-software-development)
- [Chapter 8: Defensive Programming](#chapter-8-defensive-programming)
- [Chapter 20: The Software-Quality Landscape](#chapter-20-the-software-quality-landscape)
- [Chapter 21: Collaborative Construction](#chapter-21-collaborative-construction)
- [Chapter 22: Developer Testing](#chapter-22-developer-testing)
- [Chapter 24: Refactoring](#chapter-24-refactoring)
- [Chapter 25: Code-Tuning Strategies](#chapter-25-code-tuning-strategies)
- [Chapter 32: Self-Documenting Code](#chapter-32-self-documenting-code)
- [Chapter 33: Personal Character](#chapter-33-personal-character)
- [Chapter 34: Themes in Software Craftsmanship](#chapter-34-themes-in-software-craftsmanship)
## Chapter 1: Software Construction
Construction - process of building (planning, designing, checking the work). Construction is mostly coding and debugging
but also involves designing, planning, unit testing, ... Centre of the software development process. The only activity
that is guaranteed to be done (planning might be imperfect, etc.).
## Chapter 2: Metaphors for a Richer Understanding of Software Development
Metaphors contribute to a greater understanding of software-development issues - paper writing metaphor, farming
metaphor, etc...
## Chapter 8: Defensive Programming
Protecting yourself from "cruel world of incorrect data". Use assertions to document assumptions made in the code.
Guidelines:
- use assertions for conditions that should never occur, this is not error checking code. On error program should take
corrective actions, on assertion fail source code should be updated.
- no executable code in asserts:
- bad: `assert foo(), ...`
- good: `result = foo(); assert result, ...`
- use asserts to document and verify preconditions (before executing the routine) and post conditions (after executing
the routine)
- for high robustness: failed assertions should be handled anyway
Error handling:
- return neutral value - 0, empty string, ...
- substitute the next piece of valid data - for example when processing stream of data from the sensor (e.g.
temperature)
, you may want to skip the missing value and wait for another
- return the same answer as the previous time - some data might not change in time dramatically, so it is okay to return
the last correct value
- substitute the closest legal value - for example reversing car does not show negative speed value but instead shows
0 (the closest legal value)
- log a warning message on incorrect data
- return error code - report error has been encountered and trust some other routine higher up will handle the error
- call centralised error-processing routine, disadvantage is that entire program coupled with the mechanism
- display error message to the user, warning: don't share too much with the user, attacker may use this information
- shut down - useful in safety-critical applications
While handling errors you need to choose between robustness (do something to keep the software alive) and correctness (
ensuring the data is always correct). Once approach is selected it should be coherent across the system.
Exceptions:
- they eliminate the possibility to go unnoticed
- throw only for truly exceptional situations - for situations that can not be addressed
- if exception can be handled locally - handle locally
- avoid exceptions in constructors, because if exception happens there, destruction might not be called - resource leak!
- include all the information that led to the exception
- avoid empty catch blocks
- standardise project's use of exceptions
Barricades:
- similar to having isolated compartments in the hull of a ship, damaged parts are isolated
- use validation classes that are responsible for cleaning the data
- assume data is unsafe and you need to sanitise it
*Offensive programming* - exceptional cases should be handled in a way that makes them obvious during development and
recoverable when production code is running. During development, you want errors to be as visible as possible but during
production it should not be observable.
## Chapter 20: The Software-Quality Landscape
There are many quality metrics: correctness, usability, efficiency, reliability, integrity, adaptivity, accuracy,
robustness - these are metrics important to the user, for a programmer more important metrics are: maintainability,
flexibility, portability, reusability, readability, testability, understandability.
*Techniques for Improving Software Quality*: set up software quality objectives, perform quality assurance activities,
prototyping.
Defect-detection techniques: design reviews, code reviews, prototyping, unit tests, integration tests, regression tests,
... even all of them combined will not detect all the issues.
> Most studies have found that inspections are cheaper than testing. A study at the Software Engineering Laboratory
> found that code reading detected about 80% more faults per hour than testing.
Cost of detection is only one part. There is also cost of fixing the issues. The longer defect remains in the system,
the more expensive it becomes to remove.
Recommended combination: Formal inspections of all requirements, architecture, design -> Modeling / prototyping -> Code
reading -> Testing.
Remember: Improving quality reduces development cost.
## Chapter 21: Collaborative Construction
> IBM found that each hour of inspection prevented about 100 hours or related work (testing and defect correction)
> Reviews cut the errors by over 80%
> Reviews create a venue for more experienced and less experienced programmers to communicate about technical issues.
Collective ownership - code is owned by the group rather than by the individuals and can be accessed and modified by
various members.
Guide on pair programming:
- it will not be effective if you argue on styling conventions
- don't let it turn into watching - person without the keyboard should be an active participant
- sometimes it is better to discuss something on the whiteboard and then go programming solo
- rotate pairs
- match other's pace, the fast learner needs to slow down
- don't force people who don't like each other to pair
- no pairing between newbies
Nice idea: for discussing the design everyone should come with a prepared list of potential issues. It is good to assign
perspectives - maintainer, coder, user, designer. Author in such discussion should play minor role, should only present
the overview. Reviewer can be anyone outside author - tester, developer. Management should not be present at the
meeting, however should be briefed with the results after the discussion. Design review can not be used for performance
appraisals. Group should be focused on identifying defects. Goal of this meeting is not to explore alternatives ar
debate who is right and who is wrong.
> NASA's Software Engineering Laboratory found that code reading detected about 3.3 defects per hour of effort. Testing
> detected 1.8 errors per hour.
## Chapter 22: Developer Testing
> You must hope to find errors in your code. Such hope might seem like an unnatural act, but you should hope that it's
> you who finds the errors and not someone else.
Why TDD:
- same effort to write test cases before and after
- you detect defects earlier, and you can correct them more easily
- forces you to think a little about the requirements and design before writing code
- exposes requirements problems sooner
Developers tend to write *clean tests* rather than test for all the ways code breaks. Developer's testing isn't
sufficient to provide adequate quality assurance.
General Principle of Software Quality: improving quality improves the development schedule and reduces development cost.
## Chapter 24: Refactoring
The Cardinal Rule of Software Evolution: Evolution should improve the internal quality of the program.
Signs / smells that indicate refactoring is needed:
- code duplication - you need to do parallel changes
- too long routine
- too long loop or too deeply nested
- poor class cohesion - if a class takes ownership for many unrelated responsibilities
- too many parameters
- changes require parallel modifications to multiple classes
- related data not organised into classes
- overloaded primitive data type
- class doesn't do much - sometimes the result of refactoring is that an old class doesn't have much to do
- trap data - one routine just passes data to another
- one class knows too much about the other
- poor names
- public data members - in general bad idea
- subclass uses only a small percentage of its parent routines
- comments should not be used to explain bad code - "don't comment bad code, rewrite it"
- usage of setup code before routine call
- code that "seems like it might be needed one day" - programmers are rather bad at guessing what functionality might be
needed someday, *design ahead* introduces unnecessary complexity
Data-Lever Refactoring:
- replace magic number with a named constant
- give a variable informative name
- inline expressions
- replace expression with a routine
- convert data primitive to a class
- encapsulate returned collection
Statement-Level Refactoring:
- decompose boolean expression - use variables that help document the meaning of the expression
- move boolean expression into a well-named function
- return as soon as you know the return value
Routine-Level Refactoring:
- Inline simple routines
- Convert long routine into a class
- Separate query operations from modification operations
- Combine similar routines by parametrizing themIf routine depends on the parameter passed in - consider splitting the
routine
- Pass a whole object instead of specific fields, however if you are creating an object just to pass it to a routine,
consider changing the routine to take only specific fields
- Routine should return the most specific object (mostly applicable to iterators, collections, ...)
Class Implementation Refactoring:
- Extract specialised code into a subclass - if class has code that is used by only a subset of its instances
- Combine similar code into a superclass - if at least 2 classes have similar code
Class Interface Refactoring:
- Eliminate classes not doing too much
- Hide a delegate - A calling B, A calling C, when really class A should call B and class B should call class C
- Or remove middleman, remove B and make A call C directly
- Hide routines that are not intended to be used outside the class
- Encapsulate unused routines - if you use only small portion of class's interface
Refactoring might cause a lot of harm if misused:
- refactoring should be small
- one refactoring at a time
- make a list of needed steps
- make a parking lot - in the middle of refactoring you might think about another refactoring, and another, and so on,
for changes that aren't required immediately save a list of TODO changes
- check IDE / compiler / other tool's errors
- refactored code should be retested, programmer should also add more test cases
- be careful about small refactoring because they tend to introduce more bugs than big refactoring
- adjust approach basing on the risk of the refactoring - some changes are more dangerous than the other
Refactoring refers to making a changes in working code and do not affect the program's behaviour. Programmers who are
tweaking broken code aren't refactoring - they are hacking.
There are many strategies on where refactoring should be started. For example, whenever you are adding a routine you
should refactor it's neighbour, or when you are adding a class, or you should refactor error-prone modules, the most
complex modules, etc.
## Chapter 25: Code-Tuning Strategies
Code tuning is one way of improving a program's performance. You can find other ways to improve performance - faster and
without harm to the code.
> More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other
> single reason - including blind stupidity ~ Wulf
Efficiency can be seen from many viewpoints:
- requirements
TRW required sub-second response time - this led to highly complex design and cost ~100M $, analysis determined, users
would be satisfied with 4 seconds responses 90% of time, modifying the response time requirements reduced cost by ~70M
$.
Before you invest time solving a performance problem, make sure you are solving a problem that needs to be solved.
- design
Sometimes program design make it difficult to write high-performance system, others make it hard not to.
- class and routine design
On this level algorithms and data structures matter.
- OS interactions
You might not be aware the compiler generated code using heavy OS calls.
- code compilation
Good compilers, turn good high-level language code into optimised machine code.
- hardware
Sometimes cheapest and the beast way to improve a program's performance is to buy a new hardware.
- code tuning
Small-scale changes that affect a single class, routine or just few lines of code, that make it run mode efficiently.
Some sources say, you can multiply improvements on each of the six levels, achieving performance improvement of a
million-fold.
Code tuning is not the most effective way to improve performance! Writing micro-efficient code does not prove you are
cool. Efficient code isn't necessarily better.
The Pareto Principle: Also known as 80/20 rule, you can get 80% of the result with 20% of effort.
Working toward perfection might prevent completion. Complete it first, and then perfect it. The part that needs to be
perfect is usually small.
False statement: Reducing the lines of code in a high level-language improves the speed or size of the resulting machine
code.:
```
# This is slower:
for i = 1 to 10
a[i] = i
# This is faster:
a[1] = 1
a[2] = 2
...
a[10] = 10
```
It is also impossible to identify performance bottlenecks before program is working completely, hence "You should
optimise as you go" is false. Also, premature optimisation is the root of all evil, because you are missing perspective.
Compilers are really powerful, however they are better in optimising straightforward code than they are at optimising
tricky code. So, design application properly, write clear code and compiler will do the rest :)
Sources of inefficiency:
- I/O operations - if possible: store data in the memory
- paging - an operation that causes the OS to swap pages of memory is much slower than operation that works on only one
page of memory.
- system calls - calls to system routines are expensive (context switch, saving app state, recovering kernel state),
avoid using system calls, write your own routines using small part of the functionality offered by a system routine,
work with system vendor to improve performance
- interpreted languages - :(
- errors - errors in code can be another source of performance problems
Experience doesn't help with optimisation. A person's experience might have come from an old machine, language or
compiler. You can never be sure about the effect of an optimisation until you measure the effect.
## Chapter 32: Self-Documenting Code
Unit development folder - informal document that contains notes used by a developer during construction - main purpose
is to provide a trail of design decisions that aren't documented elsewhere.
Detailed-design document - low-level design document, describes the class-level or routine-level design decisions.
Internal documentation (within the program) is the most detailed kind of documentation. The main contributor to
code-level documentation isn't comments, but good programming style, good variable names, clear layout and minimisation
of control-flow and data-structure complexity,
> **Good comments don't repeat the code or explain it. They clarify its intent. Comments should explain, at a higher
> level of abstraction than the code, what you are trying to do.**
Kinds of comments:
- repeat of the code - comment gives no additional information
- explanation of the code - code is so complicated it needs to be explained, make code better instead of adding comments
- **summary of the code** - very useful when someone other than the code's original author tries to modify the code
- **description of the codes' intent** - IBM study says "understanding programmer's intent is the most difficult
problem"
- **information that cannot be expressed by code itself** - for example copyright notice, notes about design, references
to requirements
3 types of acceptable comments were highlighted above.
Effective commenting shouldn't be time-consuming. Guidelines for effective commenting:
- if commenting style is too fancy it very likely becomes annoying to maintain
- write pseudocode in comments
- performance is not a good reason for avoiding commenting (in some languages commenting slows down execution /
compilation) - usually solution for this is to pass code through tool striping comments before release
End-line comments pose several problems and should be avoided - hard to write meaningful comment in one line, not much
space on the right side of the screen.
The code itself is always the first documentation you should check. If the code is not good enough, look for comments.
Comments should avoid abbreviations. Comments should justify violations of good programming style. Don't comment tricky
code, rewrite it. If something is tricky for you, for others it might be incomprehensible.
> Make your code so good that you don't need comments, and then comment it to make it even better.
Commenting data declarations:
- comment the units
- comment the range of allowable numeric values
- use enumerated types to express coded meanings
- comment limitations of input data, use assertions
- if variable is used as bit field, explain every bit
- if you have comments that refer to a specific variable, make sure the comment stays updated after variable name change
Keep comments close to the code they describe. Describe the design approaches, limitations, usage assumptions and so on.
Do not document implementation details in the interface.
## Chapter 33: Personal Character
The best programmers are the people who realise how small their brains are. The purpose of many good programming
practices is to reduce the load on your grey cells:
- decomposing - make a system simpler to understand
- reviews, inspections and tests - our intellectual capacity is limited, so we augment it with someone's else
- short routines reduce the load on our brains
- writing programs in terms of the problem domain rather than in terms of low level implementation details reduces
mental workload
- conventions free brain from the relatively mundane aspects of programming
How to exercise curiosity and make a learning a priority?
- If your workload consists entirely on short-term assignments that don't develop your skills, be dissatisfied. Half of
what you need to know will be outdated in three years. You are not learning, you are turning into a dinosaur. If you
can't learn at your job, find a new one.
- Experiment if you don't understand something. Learn to make mistakes, learn from the each. Making a mistake is no sin.
Failing to learn from mistake is.
- Read about problem-solving, don't reinvent the wheel.
- Study the work of the great programmers, it is not about reading 500-long source code but for example about high-level
design.
- Read books, one book is more than most programmers read each year.
- Affiliate with other professionals
- Set up a professional development plan
Mature programmers are honest, which means: you refuse to pretend you are an expert when you are not, you admit your
mistakes, you provide realistic estimates, you understand your program.
Writing readable code is part of being a team player. As a readability guideline, keep the person who has to modify your
code in mind. Programming is communicating with another programmer first and communicating with the computer second.
To stay valuable, you have to stay current. For young hungry programmers, this is an advantage. Older programmers
sometimes feel they have already earned their stripes and resent having to improve themselves year after year.
Good habits matter because most of what you do as a programmer you do without consciously thinking about it.
## Chapter 34: Themes in Software Craftsmanship
There are many intellectual tools for handling computer science complexity:
- dividing a system into subsystems at the architecture level so that brain can focus on smaller amount of the system at
one time
- carefully interface definition
- preserving the abstraction representing by the interface so that brain doesn't have to remember arbitrary details
- avoid global data
- avoid deep inheritance hierarchy
- carefully define error handling strategy
- prevent monster classes creation
- keep functions short
- use self-explanatory names
- minimise number of parameters passed to the routine
- use conventions
Points above are used to decrease usage of mental resources you need to use in order to understand the code.
Abstraction is a particularly powerful tool for managing complexity. Fred Brooks said that the biggest single gain ever
made in computer science was in the jump from machine language to higher-level languages. It freed programmers from
worrying about detailed quirks of individual pieces of the hardware and allowed them to focus on programming.
Reducing complexity is arguably the most important key to being and effective programmer.
Collective ability isn't simply the sum of the team members' individual skills. The way people work together determines
if abilities sum up or subtract from each other.
In real word, requirements are never stable -in order to build software more flexibly - use incremental approach, plan
to develop program in several iterations.
Write readable code because it helps other people to read the code. Computer doesn't care if code is readable. A
professional programmer writes readable code. Even if you think you are the only one who will read your code, in
reality, chances are good that someone else will need to modify your code. One study found that 10 generations of
maintenance programmers work on an average program before it gets rewritten.
If your language doesn't support some mechanisms do not hesitate and implement them (e.g. missing `assert`) on your own.
At the highest level, you shouldn't have any idea how the data is stored. Suggested levels of abstraction:
4. High level problem domain terms
3. low level problem domain terms
2. low level implementation structures
1. programming language structures and tools
0. operating system operations and machine instructions
================================================
FILE: books/comic-agile.md
================================================
[go back](https://github.com/pkardas/learning)
# Comic Agilé
Book by Luxshan Ratnaravi, Mikkel Noe-Nygaard
- [1: Transformation](#1-transformation)
- [4: Team](#4-team)
- [6: Miscellaneous](#6-miscellaneous)
## 1: Transformation
Instead of taking a waterfall approach to your agile transformation, take an iterative one and grow the scope
organically. Focus on changing the organizational culture to align with an agile one.
Product Owners don't dictate anything just because they are accountable for maximizing the value through effective
product backlog. The entire Scrum Team collaborates on creating a plan for the next Sprint.
Asses the psychological safety in your organization. If it is too low, seek to make working agreements where blameless
post-mortems are part of them, so you can create a culture of promoting healthy conflicts and celebration of mistakes (
and learning from them). Help your managers in demanding more psychological safety from their superiors, as that is a
prerequisite for the managers creating it for you.
If you only partly adopted the agile way of working, the scope and time might be fixed, so the only parameter that the
teams can rally vary is how much technical debt to create.
## 4: Team
Team Velocity - the velocity is only for the team. If Management doesn't get that, educate them on the purpose and
nature of velocity.
Technical Debt - If you PO doesn't get the importance of reducing Technical Debt, you need to educate them - spending
some time now on reducing the technical debt will most likely decrease time-to-market of new features.
Avoid external participants in the team's retrospective (lack of trust to the externals).
Use a simple tools for building agile culture, by taking a just-enough approach to your tooling, you free ip energy to
focus on the needed behavioral changes.
DevOps is not just about tools, testing and CI/CD pipelines - it is more about culture, breaking down silos and
aligning cross-functional teams tp the paths of value delivery.
WIP limit should create a pull system in the team's flow. This should then bring a conversation about collaboration and
the knowledge sharing needed to ensure that the entire team can actually swarm around each PBI.
Mob Programming - is about working collaboratively in groups of +3 to deliver high quality software and/or share
knowledge between the developers in the mob. The Driver - controls the keyboard, the Navigators are thinking,
discussing, reviewing and reflecting. The roles are interchanged.
Stability is the foundation for building the trust needed to become high-preforming teams. If team keeps changing, they
will have difficulties moving up Tucksman's phases - forming, storming, norming, performing.
## 6: Miscellaneous
In the spirit of openness, you don't have to wait for the Retrospective to bring up potential improvements to your ways
of working.
Companies with diverse leadership are 45% more likely to grow their market share and 70% more likely to capture new
markets compared to companies with "non-diverse" leadership. Behavioral diversity is the other half of the equation,
which includes:
- ensuring everyone is heard
- making it safe to propose novel ideas
- giving team members decision-making authority
- sharing credit for success
- giving actionable feedback
- implementing feedback from the team
================================================
FILE: books/cracking-coding-interview/Dockerfile
================================================
FROM python:3.10.4
WORKDIR /src
ENV PYTHONPATH "${PYTHONPATH}:/src"
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY src/ src/
================================================
FILE: books/cracking-coding-interview/docker-compose.yml
================================================
version: "3.9"
services:
interview:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./:/src
================================================
FILE: books/cracking-coding-interview/notes.md
================================================
[go back](https://github.com/pkardas/learning)
# Cracking the Coding Interview: 189 Programming Questions and Solutions
Book by Gayle Laakmann McDowell
Code here: [click](.)
- [Chapter 1: The Interview Process](#chapter-1-the-interview-process)
- [Chapter 2: Behind the Scenes](#chapter-2-behind-the-scenes)
- [Chapter 3: Special situations](#chapter-3-special-situations)
- [Chapter 4: Before the Interview](#chapter-4-before-the-interview)
- [Chapter 5: Behavioral Questions](#chapter-5-behavioral-questions)
## Chapter 1: The Interview Process
Assessment of a candidate performance:
- Analytical skills: Did you need much help to solve the problem? How optimal was your solution? How long did it take
you to arrive at a solution?
- Coding skills: Were you able to successfully translate your algorithm to reasonable code? Was it clean and
well-organized? Did you think of potential errors? Did you use good style?
- Technical knowledge: Do you have a strong foundation in computer science and the relevant technologies?
- Experience: Have you made good technical decisions in the past? Have you built interesting, challenging projects? Have
you shown drive, initiative, and other important factors?
- Culture fit: Do your personality and values fit with the company and team? Did you communicate well with your
interviewer?
False negatives are acceptable. Some good candidates are rejected. The company is out to build a great set of employees.
They can accept that they miss out on some good people. Company is far more concerned with false positives: people who
do well in an interview but are not in fact very good.
Basic data structure and algorithm knowledge is useful. It is a good proxy. These skills are not hard to learn, but are
well-correlated with being a good developer. Also, it is hard to ask problem-solving questions that don't involve
algorithms and data structures.
Your interviewer develops a feel for your performance by comparing you to other people. Getting a hard question isn't a
bad thing. When it is harder for you, it is harder for everyone.
If you haven't heard back from a company within 3-5 business days after interview, check in with your recruiter.
You can almost always re-apply to a company after getting rejected. Typically, you have to wait between 6-12 months.
## Chapter 2: Behind the Scenes
"Bar raiser" interviewer is charged with keeping the interview bar high. This person has significant experience with
interviews and veto power in the hiring decision.
## Chapter 3: Special situations
**Experienced candidates.** More experienced engineers might see slightly less focus on algorithm questions. Some
interviewers might hold experienced candidates to a somewhat lower standard. After all, it has been years since these
candidates took an algorithms class. Others though hold experienced candidates to a higher standard. On average, it
balances out.
The exception to this rule is system design and architecture questions. Performance in such interview questions would be
evaluated with respect to your experience level.
Personality fit: Typically assessed by how you interact with your interviewer. Establishing a friendly, engaging
conversation with your interviewers is your ticket to many job offers.
**For interviewers.**
- Don't actually ask the exact questions in here (this book). You can ask similar questions to these. Some candidates
are reading this book. Your goal is to test their problem-solving skills, not their memorization skills.
- Ask Medium and Hard problems. When you ask questions that are too easy, performance gets clustered together.
- Use hard questions, not hard knowledge. If your question expects obscure knowledge, ask yourself: is this truly an
important skill? Most won't remember Dijkstra's algorithm or the specifics of how AVL trees work.
- Avoid "scary" questions. Some questions intimidate candidates, because it seems like they involve some specialized
knowledge, even if they really don't - math or probability, low-level knowledge, system design or scalability,
proprietary systems (e.g. Google Maps). If you are going to ask a question that sounds "scary", make sure you really
reassure candidates that it doesn't require the knowledge that they think it does.
- Offer positive reinforcement. You want candidates to feel comfortable. A candidate who is nervous will perform poorly,
and it doesn't mean that they aren't good. Moreover, a good candidate who has a negative reaction to you or to the
company is less likely to accept an offer - and they may dissuade their friends from interviewing/accepting as well.
No matter how poorly a candidate is doing, there is always something they got right. Find a way to infuse some
positivity into the interview.
- Coach your candidates.
- Many candidates don't use an example to solve a question. Guide them.
- Some candidates take a long time to find the bug because they use an enormous example. They didn't realize it
would be more efficient to analyze their code conceptually first, or that a small example would work nearly as
well. Guide them.
- If they dive into code before they have an optimal solution, pull them back and focus them on the algorithm.
- If they get nervous and stuck and aren't sure where to go, suggest to them that they walk through the brute force
solution and look for areas to optimize.
- Remind them that they can start off with a brute solution. Their first solution doesn't have to be perfect.
- If they want silence, give them silence. If your candidate needs this, give your candidate time to think.
- Know your mode: sanity check, quality, specialist, and proxy.
- Sanity Check - Easy problem-solving or design questions. They assess a minimum degree of competence. You can use
them early in the process.
- Quality Check - More challenging questions. Designed to be more rigorous and make a candidate think.
- Specialist Questions - Test knowledge on specific topics, e.g. Java or machine learning.
- Proxy Knowledge - This is knowledge that is not quite at the specialist level, but that you would expect a
candidate at their level to know.
## Chapter 4: Before the Interview
If you are smart, you can code, and you can prove that, you can land your interview.
Resume screeners want to know that you are smart, and you can code. You should prepare your resume to highlight these 2
things. Think twice before cutting more technical lines in order to allow space for your non-technical hobbies.
Keep your resume short, max. 1.5-2 pages. Long resumes are not a reflection of having tons of experience, there are a
reflection of not understanding how to prioritize content. A resume should not include a full history of every role you
have ever had. Include only the relevant positions - the ones that make you a more impressive candidate.
For each role, try to discuss you accomplishments with the following approach: "_Accomplishment X by implementing Y
which led to Z_". Not everything will fit into this approach, but the principle is the same: what you did, how you did
it, and what the results were.
## Chapter 5: Behavioral Questions
Ensure that you have one to three projects that you can talk about in detail. You should be able to discuss the
technical components in depth. These should be projects where you played a central role.
What are your weaknesses? A good answer conveys a real, legitimate weakness but emphasises how you work to overcome it.
What questions should you ask the interviewer?
- Genuine Questions: these are the questions you actually want to know the answer to.
- Insightful Questions: these questions demonstrate your knowledge or understanding of technology. These questions will
typically require advance research about the company.
- Passion Questions: these questions are designed to demonstrate your passion for technology. They show that you are
interested in learning and will be a strong contributor to the company. E.g.: I am very interested in scalability, and
I would love to learn more about it. What opportunities are there at this company to learn about this?
Be specific, not arrogant. How do you make yourself sound good without being arrogant? Be specific. Specificity means
giving just the facts and letting the interviewer derive an interpretation.
Stay light on details and just state the key points. Your interviewer can ask for more details.
Focus on yourself, not your team. More "I", less "we".
Give structured answers.
1. Nugget first - means starting your response with a "nugget" that succinctly describes what your response will be
about.
2. S.A.R (Situation, Action, Result) - you start off outlining the situation, then explaining the actions you took, and
lastly, describing the result.
Tell me about yourself, suggested structure:
1. Current role (headline only)
2. College
3. Post college & onwards (job, technologies)
4. Current role (more details)
5. Outside of work (hobbies)
6. Wrap up (what are you looking for)
================================================
FILE: books/cracking-coding-interview/requirements.txt
================================================
pytest==7.1.2
================================================
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/check_permutation.py
================================================
import pytest
def check_permutation_sets(string: str, potential_permutation_string: str) -> bool:
return len(string) == len(potential_permutation_string) and set(string) == set(potential_permutation_string)
def check_permutation_sort(string: str, potential_permutation_string: str) -> bool:
return sorted(string) == sorted(potential_permutation_string)
def check_permutation_array(string: str, potential_permutation_string: str) -> bool:
if len(string) != len(potential_permutation_string):
return False
url_array = [0] * 128
for ch in string:
url_array[ord(ch)] += 1
for ch in potential_permutation_string:
url_array[ord(ch)] -= 1
if url_array[ord(ch)] < 0:
return False
return True
@pytest.mark.parametrize("string, potential_permutation_string, is_permutation", [
# @formatter:off
("god", "dog", True),
("god", "dod", False),
("god", "dogg", False),
("cat belongs to ala", "ala belongs to cat", True),
("interview questions", "interviews question", True),
("interview questions", "interview question", False),
# @formatter:on
])
@pytest.mark.parametrize("function", [
check_permutation_sets,
check_permutation_sort,
check_permutation_array,
])
def test_algorithm(function, string, potential_permutation_string, is_permutation):
assert function(string, potential_permutation_string) == is_permutation
================================================
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/is_unique.py
================================================
import pytest
def check_if_has_unique_characters_pythonic(string: str) -> bool:
return len(set(string)) == len(string)
def check_if_has_unique_characters_ascii(string: str) -> bool:
boolean_array = [False] * 128
for ch in string:
int_ch = ord(ch)
if boolean_array[int_ch]:
return False
boolean_array[int_ch] = True
return True
def check_if_has_unique_characters_no_structures(string: str) -> bool:
for i, ch_0 in enumerate(string):
for ch_1 in string[i + 1:]:
if ch_0 == ch_1:
return False
return True
def check_if_has_unique_characters_no_structures_sort(string: str) -> bool:
sorted_string = sorted(string)
for i in range(len(sorted_string) - 1):
if sorted_string[i] == sorted_string[i + 1]:
return False
return True
@pytest.mark.parametrize("string, has_all_unique_chars", [
# @formatter:off
("qwerty", True),
("", True),
("qqwert", False),
("qwertt", False),
# @formatter:on
])
@pytest.mark.parametrize("function", [
check_if_has_unique_characters_pythonic,
check_if_has_unique_characters_ascii,
check_if_has_unique_characters_no_structures,
check_if_has_unique_characters_no_structures_sort,
])
def test_algorithm(function, string, has_all_unique_chars):
assert function(string) == has_all_unique_chars
================================================
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/one_away.py
================================================
import pytest
def is_one_edit_away_pythonic(string: str, edit: str) -> bool:
if abs(len(string) - len(edit)) > 1:
return False
if string in edit or edit in string:
return True
return len(set(string) - set(edit)) <= 1
def is_one_edit_away_loop(string: str, edit: str) -> bool:
if abs(len(string) - len(edit)) > 1:
return False
shorter_text, longer_text = string if len(string) < len(edit) else edit, string if len(string) >= len(edit) else edit
shorter_i, longer_i = 0, -1
edit_found = False
while shorter_i < len(shorter_text) and longer_i < len(longer_text):
longer_i += 1
if shorter_text[shorter_i] == longer_text[longer_i]:
shorter_i += 1
continue
if edit_found:
return False
if len(string) == len(edit):
shorter_i += 1
edit_found = True
return True
@pytest.mark.parametrize("string, edit, expected_result", [
# @formatter:off
("pale", "ple", True),
("pale", "ale", True),
("ale", "pale", True),
("pales", "pale", True),
("pale", "bale", True),
("pale", "bake", False),
("pale", "ba", False),
# @formatter:on
])
@pytest.mark.parametrize("function", [
is_one_edit_away_pythonic,
is_one_edit_away_loop
])
def test_algorithm(function, string, edit, expected_result):
assert function(string, edit) == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/palindrome_permutation.py
================================================
from collections import Counter
import pytest
def is_palindrome_permutation_pythonic(string: str) -> bool:
raw_string = string.replace(' ', '')
letter_frequency = Counter(raw_string)
if len(raw_string) % 2 == 0:
return all(frequency % 2 == 0 for frequency in letter_frequency.values())
else:
return sum(1 for frequency in letter_frequency.values() if frequency == 1) <= 1
def is_palindrome_permutation_counter(string: str) -> bool:
raw_string = string.replace(' ', '')
letter_frequency = Counter()
num_of_odd = 0
for ch in raw_string:
letter_frequency[ch] += 1
if letter_frequency[ch] % 2 == 1:
num_of_odd += 1
else:
num_of_odd -= 1
return num_of_odd <= 1
@pytest.mark.parametrize("string, expected_result", [
# @formatter:off
("tact coa", True),
("kamil slimak", True),
("slimakkamil ", True),
("aaaaaab", True),
("aaa", True),
("aaaaacb", False),
("abc", False),
("slimakoamil ", False),
# @formatter:on
])
@pytest.mark.parametrize("function", [
is_palindrome_permutation_pythonic,
is_palindrome_permutation_counter
])
def test_algorithm(function, string, expected_result):
assert function(string) == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/rotate_matrix.py
================================================
from typing import List
import pytest
def rotate_matrix_list_comprehension(matrix: List[List[int]]) -> List[List[int]]:
size = len(matrix)
return [
[matrix[col][row] for col in reversed(range(size))]
for row in range(size)
]
def rotate_matrix_zip(matrix: List[List[int]]) -> List[List[int]]:
return [list(reversed(row)) for row in zip(*matrix)]
@pytest.mark.parametrize("matrix, rotated_matrix", [
([[1, 2],
[3, 4]],
[[3, 1],
[4, 2]]),
([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]],
[[7, 4, 1],
[8, 5, 2],
[9, 6, 3]]),
([[1, 2, 3, 8],
[4, 5, 6, 8],
[7, 8, 9, 8],
[8, 8, 8, 8]],
[[8, 7, 4, 1],
[8, 8, 5, 2],
[8, 9, 6, 3],
[8, 8, 8, 8]])
])
@pytest.mark.parametrize("function", [
rotate_matrix_zip,
rotate_matrix_list_comprehension
])
def test_algorithm(function, matrix, rotated_matrix):
assert function(matrix) == rotated_matrix
================================================
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/string_compression.py
================================================
from dataclasses import dataclass
import pytest
def compress_string(text: str) -> str:
@dataclass
class Compressed:
char: str
freq: int
compressed = []
for ch in text:
if compressed and ch == compressed[-1].char:
compressed[-1].freq += 1
else:
compressed.append(Compressed(char=ch, freq=1))
return ''.join(f"{c.char}{c.freq}" for c in compressed) if len(compressed) * 2 < len(text) else text
@pytest.mark.parametrize("text, expected_result", [
# @formatter:off
("a", "a"),
("aabb", "aabb"),
("aaaa", "a4"),
("aabbb", "a2b3"),
("aabbbaa", "a2b3a2"),
# @formatter:on
])
def test_algorithm(text, expected_result):
assert compress_string(text) == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/string_rotation.py
================================================
import pytest
def is_rotated(string: str, rotated_string: str) -> bool:
return len(string) == len(rotated_string) and rotated_string in string * 2
@pytest.mark.parametrize("string, rotated_string, expected_result", [
# @formatter:off
("", "", True),
("waterbottle", "erbottlewat", True),
("dog", "gdo", True),
("dog", "dogdo", False),
("dog", "godd", False),
("dog", "go", False),
# @formatter:on
])
def test_algorithm(string, rotated_string, expected_result):
assert is_rotated(string, rotated_string) == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/urlify.py
================================================
import pytest
def urlify_pythonic(url: str) -> str:
return ' '.join(url.split()).replace(' ', "%20")
def urlify_array(url: str) -> str:
result_url = ""
last_appended_character = None
for ch in url:
if ch == ' ' and last_appended_character is None:
# Do not duplicate '%20' in the URL
continue
elif ch == ' ' and last_appended_character:
last_appended_character = None
result_url += "%20"
else:
last_appended_character = ch
result_url += ch
if last_appended_character is None:
return result_url[:-3]
return result_url
@pytest.mark.parametrize("url, expected_url", [
# @formatter:off
("Mr John Smith", "Mr%20John%20Smith"),
("Mr John Smith", "Mr%20John%20Smith"),
(" Mr John Smith", "Mr%20John%20Smith"),
("Mr John Smith ", "Mr%20John%20Smith"),
("Mr ", "Mr"),
("M ", "M"),
(" ", ""),
("", ""),
# @formatter:on
])
@pytest.mark.parametrize("function", [
urlify_pythonic,
urlify_array,
])
def test_algorithm(function, url, expected_url):
assert function(url) == expected_url
================================================
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/zero_matrix.py
================================================
from typing import List
import pytest
def nullify_loop(matrix: List[List[int]]) -> List[List[int]]:
height, width = len(matrix), len(matrix[0])
columns, rows = set(), set()
for row in range(height):
for col in range(width):
if matrix[row][col] == 0:
columns.add(col)
rows.add(row)
return [
[
0 if row in rows or col in columns else matrix[row][col]
for col in range(width)
]
for row in range(height)
]
def nullify_in_place(matrix: List[List[int]]) -> List[List[int]]:
height, width = len(matrix), len(matrix[0])
def nullify_column(pos: int) -> None:
for i in range(height):
matrix[i][pos] = 0
def nullify_row(pos: int) -> None:
matrix[pos] = [0] * width
col_start = 0
for row in range(height):
for col in range(col_start, width):
if matrix[row][col] == 0:
nullify_row(row)
nullify_column(col)
col_start = col + 1
break
return matrix
@pytest.mark.parametrize("matrix, rotated_matrix", [
([[0, 2],
[3, 4]],
[[0, 0],
[0, 4]]),
([[1, 2, 3, 4],
[1, 0, 3, 4],
[1, 2, 3, 0]],
[[1, 0, 3, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]])
])
@pytest.mark.parametrize("function", [
nullify_loop,
nullify_in_place,
])
def test_algorithm(function, matrix, rotated_matrix):
assert function(matrix) == rotated_matrix
================================================
FILE: books/cracking-coding-interview/src/ch02_linked_lists/delete_middle_node.py
================================================
import pytest
from linked_list import (
LinkedList,
Node,
)
def delete_middle_node(node: Node) -> None:
assert node.next, "node is not the last node in the linked list"
node.data = node.next.data
node.next = node.next.next
@pytest.mark.parametrize("values, node, expected_result", [
# @formatter:off
([1, 2, 3, 4], 2, [1, 3, 4]),
([1, 2, 3, 4], 3, [1, 2, 4]),
# @formatter:on
])
def test_algorithm(values, node, expected_result):
linked_list = LinkedList(values)
delete_middle_node(linked_list.node_for_value(node))
assert linked_list.values == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch02_linked_lists/intersection.py
================================================
from typing import Optional
import pytest
from linked_list import (
LinkedList,
Node,
)
def intersection(list_0: LinkedList, list_1: LinkedList) -> Optional[Node]:
if list_0.tail != list_1.tail:
return None
l0_node, l1_node = list_0.head, list_1.head
l0_len, l1_len = list_0.length, list_1.length
# Advance pointers when lists have different size:
if l0_len > l1_len:
for i in range(l0_len - l1_len):
l0_node = l0_node.next
if l0_len < l1_len:
for i in range(l1_len - l0_len):
l1_len = l1_len.next
while l0_node and l1_node:
if l0_node == l1_node:
return l0_node
l0_node = l0_node.next
l1_node = l1_node.next
assert False, "Loop above must finish the program"
l0 = LinkedList([3, 1, 5, 9])
l1 = LinkedList([4, 6])
tail = LinkedList([7, 2, 1]).head
l4 = LinkedList([3, 1, 5, 9, 7, 2, 1])
l5 = LinkedList([4, 6, 7, 2, 1])
@pytest.mark.parametrize("list_0, list_0_tail, list_1, list_1_tail, expected_result", [
# @formatter:off
(l0, tail, l1, tail, tail),
(l4, None, l5, None, None)
# @formatter:on
])
def test_algorithm(list_0, list_0_tail, list_1, list_1_tail, expected_result):
list_0.tail.next = list_0_tail
list_1.tail.next = list_1_tail
assert intersection(list_0, list_1) == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch02_linked_lists/linked_list.py
================================================
from typing import (
List,
Optional,
)
import pytest
class Node:
def __init__(self, data: int) -> None:
self.next = None
self.data = data
class LinkedList:
def __init__(self, data: List[int]) -> None:
self.head = None
for val in data:
self.append(val)
@property
def values(self) -> List[int]:
result, current = [], self.head
while current:
result.append(current.data)
current = current.next
return result
@property
def tail(self) -> Optional[Node]:
node = self.head
while node and node.next:
node = node.next
return node
@property
def length(self) -> int:
return len(self.values)
def node_for_value(self, val: int) -> Optional[Node]:
node = self.head
while node:
if node.data == val:
return node
node = node.next
return None
def append(self, data: int) -> None:
self.head = append(self.head, data)
def delete(self, data: int) -> None:
self.head = delete(self.head, data)
def delete(head: Optional[Node], data: int) -> Optional[Node]:
node = head
if not node:
return None
if head.data == data:
return head.next
while node.next:
if node.next.data == data:
node.next = node.next.next
break
node = node.next
return head
def append(head: Optional[Node], data: int) -> Optional[Node]:
if not head:
return Node(data)
current, end = head, Node(data)
while current.next:
current = current.next
current.next = end
return head
@pytest.mark.parametrize("values", [
[],
[1],
[1, 2],
[1, 2, 3],
])
def test_append(values):
assert LinkedList(values).values == values
@pytest.mark.parametrize("values, to_delete, expected_result", [
# @formatter:off
([], 0, []),
([1], 0, [1]),
([1], 1, []),
([1, 2], 1, [2]),
([1, 2], 2, [1]),
([1, 2, 3], 2, [1, 3]),
# @formatter:on
])
def test_delete(values, to_delete, expected_result):
linked_list = LinkedList(values)
linked_list.delete(to_delete)
assert linked_list.values == expected_result
@pytest.mark.parametrize("values, value, expected_node_val", [
# @formatter:off
([1, 2, 3, 4], 2, 2),
([1, 2, 3, 4], 5, None)
# @formatter:on
])
def test_node_for_value(values, value, expected_node_val):
node = LinkedList(values).node_for_value(value)
assert node.data if node else node == expected_node_val
@pytest.mark.parametrize("values, expected_tail", [
# @formatter:off
([], None),
([1], 1),
([1, 2], 2),
# @formatter:on
])
def test_tail(values, expected_tail):
tail = LinkedList(values).tail
assert tail.data == expected_tail if expected_tail else tail is None
================================================
FILE: books/cracking-coding-interview/src/ch02_linked_lists/loop_detection.py
================================================
from typing import Optional
import pytest
from linked_list import (
LinkedList,
Node,
)
def get_loop(linked_list: LinkedList) -> Optional[Node]:
slow, fast = linked_list.head, linked_list.head
def get_loop_head():
nonlocal slow, fast
slow = linked_list.head
while slow != fast:
slow = slow.next
fast = fast.next
return fast
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return get_loop_head()
return None
l0 = LinkedList([1, 2, 3, 4, 5])
l0.node_for_value(5).next = l0.node_for_value(3)
l1 = LinkedList([1, 2, 3, 4, 5])
@pytest.mark.parametrize("linked_list, expected_result", [
# @formatter:off
(l0, l0.node_for_value(3)),
(l1, None),
# @formatter:on
])
def test_algorithm(linked_list, expected_result):
assert get_loop(linked_list) == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch02_linked_lists/palindrome.py
================================================
import pytest
from linked_list import (
LinkedList,
Node,
)
def is_palindrome_simple(linked_list: LinkedList) -> bool:
values = linked_list.values
return values == values[::-1]
def is_palindrome_reverse(linked_list: LinkedList) -> bool:
def reverse_list() -> Node:
head, node = None, linked_list.head
while node:
new_node = Node(data=node.data)
new_node.next = head
head = new_node
node = node.next
return head
normal_node = linked_list.head
reversed_node = reverse_list()
while normal_node and reversed_node:
if normal_node.data != reversed_node.data:
return False
normal_node = normal_node.next
reversed_node = reversed_node.next
return not normal_node and not reversed_node
def is_palindrome_slow_fast_runner(linked_list: LinkedList) -> bool:
slow, fast = linked_list.head, linked_list.head
stack = []
while fast and fast.next:
stack.append(slow.data)
slow = slow.next
fast = fast.next.next
if fast:
slow = slow.next
while slow:
if stack and stack.pop() != slow.data:
return False
slow = slow.next
return True
@pytest.mark.parametrize("values, expected_result", [
# @formatter:off
([1, 2, 3, 4], False),
([1, 2, 2, 2], False),
([1, 2, 2, 1], True),
([1, 2, 1], True),
([1], True),
([], True)
# @formatter:on
])
@pytest.mark.parametrize("function", [
is_palindrome_simple,
is_palindrome_reverse,
is_palindrome_slow_fast_runner,
])
def test_algorithm(function, values, expected_result):
linked_list = LinkedList(values)
assert function(linked_list) == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch02_linked_lists/partition.py
================================================
from typing import Tuple
import pytest
from linked_list import LinkedList
def partition(linked_list: LinkedList, partition_val: int) -> Tuple[LinkedList, LinkedList]:
l1, l2 = LinkedList(data=[]), LinkedList(data=[])
node = linked_list.head
while node:
if node.data < partition_val:
l1.append(node.data)
else:
l2.append(node.data)
node = node.next
return l1, l2
@pytest.mark.parametrize("values, partition_val, expected_values", [
# @formatter:off
([1, 2, 3, 4, 5], 3, ([1, 2], [3, 4, 5])),
([1, 2, 3, 4, 5], 0, ([], [1, 2, 3, 4, 5])),
([1, 2, 3, 4, 5], 6, ([1, 2, 3, 4, 5], [])),
# @formatter:on
])
def test_algorithm(values, partition_val, expected_values):
linked_list = LinkedList(values)
l1, l2 = partition(linked_list, partition_val)
assert (l1.values, l2.values) == expected_values
================================================
FILE: books/cracking-coding-interview/src/ch02_linked_lists/remove_dups.py
================================================
import pytest
from linked_list import LinkedList
def remove_duplicates_buffer(linked_list: LinkedList) -> LinkedList:
unique_data = set()
prev, current = None, linked_list.head
while current:
if current.data in unique_data:
prev.next = current.next
else:
unique_data.add(current.data)
prev = current
current = current.next
return linked_list
def remove_duplicates_no_buffer(linked_list: LinkedList) -> LinkedList:
current = linked_list.head
while current:
runner = current
while runner.next:
if current.data == runner.next.data:
runner.next = runner.next.next
else:
runner = runner.next
current = current.next
return linked_list
@pytest.mark.parametrize("values, expected_result", [
# @formatter:off
([], []),
([1, 1], [1]),
([1, 1, 0], [1, 0]),
([1, 1, 1, 1], [1]),
([0, 1, 0, 1], [0, 1]),
([1, 2, 3, 4], [1, 2, 3, 4]),
# @formatter:on
])
@pytest.mark.parametrize("function", [
remove_duplicates_buffer,
remove_duplicates_no_buffer
])
def test_algorithm(function, values, expected_result):
linked_list = LinkedList(values)
assert function(linked_list).values == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch02_linked_lists/return_kth_to_last.py
================================================
from typing import Optional
import pytest
from linked_list import (
LinkedList,
Node,
)
def return_kth_to_last_simple(linked_list: LinkedList, k: int) -> int:
node = linked_list.head
position, i = len(linked_list.values) - k, 0
if position < 0:
return -1
while node and i < position:
node = node.next
i += 1
return node.data
def return_kth_to_last_simplest(linked_list: LinkedList, k: int) -> int:
values = linked_list.values
size = len(values)
return values[size - k] if size - k >= 0 else -1
def return_kth_to_last_recursive(linked_list: LinkedList, k: int) -> int:
found_value = None
def _return_kth_to_last(node: Optional[Node]) -> int:
if not node:
return 0
index = _return_kth_to_last(node.next) + 1
if index == k:
nonlocal found_value
found_value = node.data
return index
_return_kth_to_last(linked_list.head)
return found_value if found_value else -1
def return_kth_to_last_iterative(linked_list: LinkedList, k: int) -> int:
p1, p2 = linked_list.head, linked_list.head
for _ in range(k):
if not p1:
return -1
p1 = p1.next
while p1:
p1 = p1.next
p2 = p2.next
return p2.data
@pytest.mark.parametrize("values, k, expected_result", [
# @formatter:off
([1, 2, 3], 1, 3),
([1, 2, 3], 2, 2),
([1, 2, 3], 3, 1),
([1, 2, 3], 4, -1),
# @formatter:on
])
@pytest.mark.parametrize("function", [
return_kth_to_last_simple,
return_kth_to_last_simplest,
return_kth_to_last_recursive,
return_kth_to_last_iterative,
])
def test_algorithm(function, values, k, expected_result):
linked_list = LinkedList(values)
assert function(linked_list, k) == expected_result
================================================
FILE: books/cracking-coding-interview/src/ch02_linked_lists/sum_lists.py
================================================
import pytest
from linked_list import (
LinkedList,
Node,
)
def sum_lists(list_0: LinkedList, list_1: LinkedList) -> LinkedList:
result, remainder = [], 0
node_0, node_1 = list_0.head, list_1.head
def add_aligned_lists() -> None:
nonlocal node_0, node_1, result, remainder
while node_0 and node_1:
result.append((node_0.data + node_1.data + remainder) % 10)
remainder = 1 if (node_0.data + node_1.data + remainder) >= 10 else 0
node_0, node_1 = node_0.next, node_1.next
def align_remaining_list(node: Node) -> None:
nonlocal result, remainder
while node:
result.append((node.data + remainder) % 10)
remainder = 1 if (node.data + remainder) >= 10 else 0
node = node.next
add_aligned_lists()
align_remaining_list(node_0)
align_remaining_list(node_1)
if remainder:
result.append(remainder)
return LinkedList(result)
@pytest.mark.parametrize("list_0, list_1, expected_result", [
# @formatter:off
([7, 1, 6], [5, 9, 2], [2, 1, 9]),
([1, 7, 1], [3], [4, 7, 1]),
([9, 9, 9], [1], [0, 0, 0, 1]),
([7, 1], [3, 1], [0, 3]),
([7, 1], [3], [0, 2]),
# @formatter:on
])
def test_algorithm(list_0, list_1, expected_result):
list_0, list_1 = LinkedList(list_0), LinkedList(list_1)
assert sum_lists(list_0, list_1).values == expected_result
================================================
FILE: books/ddd.md
================================================
[go back](https://github.com/pkardas/learning)
# Domain-Driven Design: Tackling Complexity in the Heart of Software
Book by Eric Evans
- [Chapter 1: Crunching Knowledge](#chapter-1-crunching-knowledge)
- [Chapter 2: Communication and the Use of Language](#chapter-2-communication-and-the-use-of-language)
- [Chapter 3: Binding Model and Implementation](#chapter-3-binding-model-and-implementation)
- [Chapter 4: Isolating the Domain](#chapter-4-isolating-the-domain)
- [Chapter 5: A Model Expressed in Software](#chapter-5-a-model-expressed-in-software)
- [Chapter 6: The Life Cycle of a Domain Object](#chapter-6-the-life-cycle-of-a-domain-object)
- [Chapter 7: Using the Language: An Extended Example](#chapter-7-using-the-language-an-extended-example)
- [Chapter 8: Breakthrough](#chapter-8-breakthrough)
- [Chapter 9: Making Implicit Concepts Explicit](#chapter-9-making-implicit-concepts-explicit)
- [Chapter 10: Supple Design](#chapter-10-supple-design)
- [Chapter 11: Applying Analysis Patterns](#chapter-11-applying-analysis-patterns)
- [Chapter 12: Relating Design Patterns to the Model](#chapter-12-relating-design-patterns-to-the-model)
- [Chapter 13: Refactoring Toward Deeper Insight](#chapter-13-refactoring-toward-deeper-insight)
- [Chapter 14: Managing Model Integrity](#chapter-14-managing-model-integrity)
- [Chapter 15: Distillation](#chapter-15-distillation)
- [Chapter 16: Large-Scale Structure](#chapter-16-large-scale-structure)
## Chapter 1: Crunching Knowledge
Effective modeling:
- Binding model and the implementation
- Cultivating a language based on the model
- Developing a knowledge-rich model
- Distilling the model - drop unneeded concepts
- Brainstorming and experimenting
Effective domain modellers are knowledge crunchers (take a torrent of information and prove it for relevant trickle).
Knowledge crunching is a collaborative work, typically led by developers in cooperation with domain experts. Early
versions or prototypes feed experience back into the team and change interpretations.
All projects lack knowledge - people leave, team reorganisations happen - in general, knowledge is lost. Highly
productive teams grow their knowledge continuously - improve technical knowledge along with general domain-modelling
skills, but also seriously learn about specific domain they are working on. The accumulated knowledge makes them
effective knowledge crunchers.
Software is unable to fill in gaps with common sense - that is why knowledge crunching is important.
Example with overbooking strategy: overbooking check should be extracted from the booking functionality to be more
explicit and visible. This is example of domain modeling and securing and sharing knowledge.
## Chapter 2: Communication and the Use of Language
The domain experts and developers use different language. Experts vaguely describe what they want, developers vaguely
understand. Cost of translation, plus the risk of misunderstanding is too high. A project needs a common language.
Ubiquitous language includes: names of classes and prominent operations, terms to discuss. Model based language should
be used to describe artefacts, tasks and functionalities.
Language may change to fit the discussion better. These changes will lead to refactoring of the code. Change in the
language is change to the model.
The domain-model-based terminology makes conversations more concise, you avoid talking about low level implementation
details, instead you use high level concepts (like in the example: Itinerary, Routing Service, Route Specification
instead of cargo id, origin and destination, ...).
Play with the model as you talk about the system, find easier ways to say what you need to say, and take those new ideas
back down to the diagrams and code.
The team should use ONE and only ONE language. Almost every conversation is an opportunity for the developers and domain
experts to play with the model, deepen understanding and fine tune it.
Domain model is something between business terms developers don't understand and technical aspect of the design.
The vital detail about the design in captured in the code. Well written implementation should be transparent and reveal
the model underlying it. The model is not the diagram, diagrams help to communicate and explain the model.
Extreme Programming advocates using no extra design documents at all (usually because the fall out of sync) - the code
should speak for itself. This motivates developers to keep code clean and transparent.
However, if document exists, it should not try to do what code already does well - document should illuminate meaning,
give insight into large-scale structures, clarify design intent, complement the code and the talking.
## Chapter 3: Binding Model and Implementation
Tightly relating the code to an underlying model gives the code meaning and makes the model relevant. Design must map to
the domain model, if not, the correctness of the software is suspect.
Model-Driven Design - discards the dichotomy of analysis model and design to search out a single model that serves both
purposes (ubiquitous language). Each object in the design plays a conceptual role described in the model. Model needs to
be revised to reflect the model in a very literal way, so mapping is obvious. The code becomes expression of the model.
Model-Driven Design is hard to accomplish in procedural languages like C or Fortran. This approach is reserved for
object-oriented programming languages.
Implementation model should not be exposed to the user.
People responsible for the implementation should participate in modeling. Strict separation of responsibilities is
harmful. Modeling and implementation are couples in model-driven design. Any technical person contributing to the model
must spend some time touching the code. Every developer must be involved in some level of discussion about the model.
## Chapter 4: Isolating the Domain
Layered Architecture - the essential principle is that any element of a layer depends only on other elements in the same
layer or on elements of the layers beneath it. Each layer specialises in a particular aspect of a computer program. Most
commonly used layers:
- UI (Presentation) Layer - showing information to the user and interpreting the user's commands.
- Application Layer - this layer does not contain business logic, but only coordinates tasks and delegates work to
collaborations of domain objects in the next layer down.
- Domain (Model) Layer - responsible for representing concepts of business, information about business situation and
business rules. This layer is the heart of business software.
- Infrastructure Layer - generic technical capabilities that support the higher layers (message sending, drawing widgets
on the UI, ...), may also support the pattern of interactions between the 4 layers through an architectural framework.
Partition a complex program into layers, develop a design within each layer that is cohesive and that depends only on
the layers below. Concentrate all the code related the domain model in one layer and isolate it from the rest of the
user interface, application and infrastructure code.
The domain models, free of the responsibility of displaying themselves, storing themselves, managing application tasks
and so forth, can be focused on expressing the domain model. This allows to evolve model to be rich enough and clear
enough to capture essential business knowledge and put it to work.
Such separation allows a much cleaner design for each layer, especially because they tend to evolve at different pace.
Upper layers can user or manipulate elements of lower ones straightforwardly by calling their public interfaces.
Domain-driven design requires only one particular layer to exist.
## Chapter 5: A Model Expressed in Software
ASSOCIATIONS. For every traversable association in the model, there is a mechanism in the software with the same
properties. Constraints on associations should be included in the model and implementation (e.g. president of ... for a
period of time), they make the model more precise and the implementation easier to maintain.
ENTITIES. Object modeling tends to lead us to focus on the attributes of an object, but the fundamental concept of an
entity is an abstract continuity threading through a life cycle and even passing through multiple forms. Sometimes such
an object must be matched with another object even though attributes differ.
Transactions in a banking application, two deposits of the same amount to the same account on the same day are still
distinct transactions. They have identity and are entities.
> When an object is distinguished by its identity, rather than its attributes, make this primary to its definition in
> the model. Keep the class definition simple and focused on life cycle continuity and identity. Define a means of
> distinguishing each object regardless of its form or history.
Identity - this may simply mean unique identifier.
Each entity must have an operational way of establishing its identity with another object - distinguishable even from
another object with the same descriptive attributes.
Defining identity demands understanding of the domain.
VALUE OBJECTS. An object that represents a descriptive aspect of the domain with no conceptual identity. These are
objects that describe things. When you care only about the attributes of an element of the model, classify it as a value
object.
SERVICES. Some concepts from the domain aren't natural to model as objects. Forcing the required domain functionality to
be the responsibility of an entity or value either distorts the definition of a model-based object or adds meaningless
artificial objects. A service is an operation offered as an interface that stands alone in the model, without
encapsulating state. The name *service* emphasises the relationship with other objects. Service have to be stateless.
MODULES. Many don't consider modules as part of the model. Yet it isn't just code being divided into modules, but
concepts. Low coupling between modules minimises the cost of understanding their place in the design. It is possible to
analyse the contents of one module with a minimum of reference to others that interact.
Choose modules that tell the story of the system and contain a cohesive set of concepts. Give the modules names that
become part of the ubiquitous language. Modules and their names should reflect insight into the domain.
Modules need to co-evolve with the rest of the model. This means refactoring modules right along with the model and
code. But this refactoring often doesn't happen.
Use packaging to separate the domain layer from other code. Otherwise, leave as much freedom as possible to the domain
developers to package the domain objects in ways that support their model and design choices.
## Chapter 6: The Life Cycle of a Domain Object
The challenges:
- Maintaining object integrity throughout the life cycle
- Preventing the model from getting swamped by the complexity of managing the life cycle
These issues can be addressed using 3 patterns.
AGGREGATES. It is difficult to guarantee the consistency of changes to object in a model with complex associations.
Invariants need to be maintained that apply to closely related groups of objects, not just discrete objects. Yet
cautious locking schemes cause multiple users to interfere pointlessly with each other and make a system unusable. An
aggregate is a cluster or associated objects that we treat as a unit for the purpose of data changes. Each aggregate has
a root and a boundary. Chose one entity to be the root of each aggregate, and control all access to the objects inside
the boundary through the root. Allow external objects to hold references to the root only.
FACTORIES. When creation of an object, or an entire aggregation, becomes complicated or reveals too much of the internal
structure, factories provide encapsulation (assembly of a car: cars are never assembled and driven at the same time,
there is no value in combining both of these functions into the same mechanism). Creation of an object can be a major
operation by itself, but complex assembly operations do not fit the responsibility of the created objects. Combining
such responsibilities can produce ungainly designs that are hard to understand. Making the client direct construction
muddies the design of the client, breaches encapsulation of the assembled object or aggregate, and overly couples the
client to the implementation of the created object.
Two basic requirements for any good factory:
1. Each creation method is atomic and enforces all invariants of the created object or aggregate.
2. The factory should be abstracted to the type desired, rather than the concrete class created
REPOSITORIES. Associations allow us to find an object based on its relationship to another. But we must have a starting
point for a traversal to an entity of value in the middle of its life cycle. For each type of object that needs global
access, create an object that can provide the illusion of an in-memory collection of all objects of that type. Set up
access through a well-known global interface. Provide methods to add and remove objects, which will encapsulate the
actual insertion or removal of data in the data store. Provide methods that select objects based on some criteria and
return objects. Provide repositories only for aggregate roots that actually need direct access. Keep the client focused
on the model, delegating all object storage and access to the repositories.
Repository provide methods that allow a client to request objects matching some criteria.
## Chapter 7: Using the Language: An Extended Example
The model organises domain knowledge and provides a language for the team. Each object in the model has a clear meaning.
To prevent domain responsibilities from being mixed with those of other parts of the system apply layered architecture.
Modeling and design is not a constant forward process. It will grind to a halt unless there is a frequent refactoring to
take advantage of new insights to improve the model and the design.
The real challenge is to actually find an incisive model, one that captures subtle concerns of the domain experts and
can drive a practical design. Ultimately, we hope to develop a model that captures a deep understanding of the domain.
Refactoring is the redesign of software in ways that do not change its functionality. Rather than doing elaborate
up-front design decisions, developers take code through a continuous series of small, discrete design changes, each
leaving existing functionality unchanged while making the design more flexible or easier to understand.
Initial models usually are naive and superficial, based on shallow knowledge. Versatility, simplicity and explanatory
power come from a model that is truly in tune with the domain.
You will usually depend on creativity and trail and error to find good ways to model the concepts you discover.
## Chapter 8: Breakthrough
The returns from refactoring are not linear. Usually there is marginal return for a small effort, and the small
improvements add up.
Slowly but surely, the team assimilates knowledge and crunches it into a model. Each refinement of code and model gives
developers a clearer view. This clarity creates the potential for a breakthrough.
Don't become paralysed trying to bring about a breakthrough. The possibility usually comes after many modest
refactorings. Most of the time is spent making piecemeal improvements, with model insights emerging gradually during
each successive refinement.
Don't hold back from modest improvements, which gradually deepen the model, even if confined with the same general
conceptual framework.
## Chapter 9: Making Implicit Concepts Explicit
A deep model has power because it contains the central concepts and abstractions that can succinctly and flexibly
express essential knowledge of the user's activities, their problems and their solutions.
The first step is to somehow represent the essential concepts of the domain in the model. Refinement comes later, after
successive iterations of knowledge crunching and refactoring. But this process really gets into gear when an important
concept is recognised and made explicit in the model and design.
Transformation of a formerly implicit concept into an explicit one is a breakthrough that leads to a deep model. More
often, though, the breakthrough comes later, after a number of important concepts are explicit in the model.
Listen to the language the domain experts use. Are there terms that succinctly state something complicated? Are they
correcting your word choice? Do the puzzled looks on their faces go away when you use a particular phrase? These are
hints of a concept that might benefit the model.
Constraints make up a particularly important category of model concepts. They often emerge implicitly, and expressing
them explicitly can greatly improve a design. Sometimes constraints find a natural home in an object or separate method.
Specification - a predicate that determines if an object does satisfy some criteria.
## Chapter 10: Supple Design
The ultimate purpose of software is to serve users. But first, that same software has to serve developers. This is
especially true in a process that emphasises refactoring.
When software with complex behaviour lacks good a design, it becomes hard to refactor or combine elements. Duplication
starts to appear as soon as a developer isn't confident of predicting the full implications of computation. Duplication
is forced when design elements are monolithic, so that the parts cannot be recombined.
Supple design is the complement to deep modelling. Once you have dug out implicit concepts and made them explicit, you
have the raw material. Thorough the iterative cycle, you hammer that material into a useful shape.
If developer must consider the implementation of a component in order to use it, the value of encapsulation is lost.
Tames should conform to the ubiquitous language so that team members can quickly infer their meaning. Write a test of a
behaviour before creating it, to force your thinking into client developer mode.
Place as much of the logic of the program as possible into functions, operations that return results with no observable
side effects.
Decompose design elements (operations, interfaces, classes and aggregates) into cohesive units, taking into
consideration your intuition of the important divisions in the domain. Align the model with the consistent aspects of
the domain that make it a viable area of knowledge in the first place.
Low coupling is fundamental to object design. When you can go all the way. Eliminate all other concepts from the
picture. Then the class will be completely self-contained and can be studied and understood alone. Every such
self-contained class significantly eases the burden of understanding a module.
Where it fits, define an operation whose return type is the same as the type of its arguments.
## Chapter 11: Applying Analysis Patterns
> Analysis patterns are groups of concepts that represent a common construction in business modelling. It may be
> relevant to only one domain, or it may span many domains.
An analysis pattern is a template for solving an organizational, social or economic problem in a professional domain.
## Chapter 12: Relating Design Patterns to the Model
Not all design patterns can be used as domain patterns.
STRATEGY - Domain models contain processes that are not technically motivated but actually meaningful in the problem
domain. When alternative processes must be provided, the complexity of choosing the appropriate process combines with
the complexity of the multiple processes themselves, and things get out of hand. Factor the varying parts of process
into a separate "strategy" object in the model. Factor apart a rule and the behaviour it governs. Implement the rule or
substitutable process following the strategy design pattern. Multiple versions of the strategy object represents
different ways the process can be done.
COMPOSITE - When the relatedness of nested containers is not reflected in the model, common behaviour has to be
duplicated at each level of the hierarchy, and nesting is rigid. Clients must deal with different levels of the
hierarchy through different interfaces, even though there may be no conceptual difference they care about. Recursion
through the hierarchy to produce aggregated information is very complicated. Define an abstract type that encompasses
all members of the composite. Methods that return information are implemented on containers to return aggregated
information about their contents. Leaf nodes implement those methods based on their own values. Clients deal with the
abstract type and have no need to distinguish leaves from containers.
## Chapter 13: Refactoring Toward Deeper Insight
Multifaceted process. There are 3 things you have to focus on:
1. Live in the domain
2. Keep looking at things a different way
3. Maintain an unbroken dialog with domain experts
Seeking insight into the domain creates a broader context for the process of refactoring.
Refactoring toward deeper insight is a continuing process. Implicit concepts are recognised and made explicit.
Development suddenly comes to the brink of a breakthrough and plunges through to a deep model.
## Chapter 14: Managing Model Integrity
Total unification of the domain model for a large system will not be feasible or cost-effective.
BOUNDED CONTEXT. Multiple models are in play on any large project. Yet when code based on distinct models is combined,
software becomes buggy, unreliable and difficult to understand. Communication among team members becomes confused. It is
often unclear in what context a model should not be applied. Therefore, explicitly define the context within which a
model applies. Explicitly set boundaries in terms of team organisation, usage within specific parts of the application.
And physical manifestations such as code bases and database schemas. Keep the model strictly consistent within these
bounds, but don't be distracted or confused by issues outside.
CONTINUOUS INTEGRATION. When a number of people are working in the same bounded context, there is a strong tendency for
the model to fragment. The bigger the team, the bigger the problem, but a few as three or four people can encounter
serious problems. Yet breaking down the system into even-smaller contexts eventually loses a valuable level of
integration and coherency. Therefore, institute a process of merging all code and other implementation artefacts
frequently, with automated tests to flag fragmentation quickly. Relentlessly exercise the ubiquitous language to hammer
out a shared view of the model as the concepts evolve in different people's heads.
CONTEXT MAP. People on other teams will not be very aware of the context sounds and will unknowingly make changes that
blur the edges or complicate the interconnections. When connections must be made between different contexts, they tend
to bleed into each other. Therefore, identify each model in play on the project and define its bounded context. This
includes the implicit models of non-object-oriented subsystems. Name each bounded context, and make the names part of
ubiquitous language. Describe the points of contact between the models, outlining explicit translation for any
communication and highlighting any sharing.
SHARED KERNEL. Uncoordinated teams working on closely related applications can go racing forward for a while, but what
they produce may not fit together. They can end up spending more on translation layers and retrofitting than they would
have on continuous integration in the first place, meanwhile duplicating effort and losing the benefits of a common
ubiquitous language. Therefore, designate some subset of the domain that the two teams agree to share. Of course this
includes, along with this subset of the model, the subset of code or of the database design associated with that part of
the model. This explicitly shared stuff has special status, and shouldn't be changed without consultation with the other
team. Integrate a functional system frequently, but somewhat less often than the pace of continuous integration within
the teams. At these integrations, run the tests of both teams.
CUSTOMER / SUPPLIER DEVELOPMENT TEAMS. The freewheeling development of the upstream team can be cramped if the
downstream team has no veto power over changes, or if procedures for requesting changes are too cumbersome. The upstream
team may even be inhibited, worried about breaking the downstream system. Meanwhile, the downstream team can be
helpless, at the mercy of upstream priorities. Therefore, establish a clear customer / supplier relationship between the
two teams. In planning sessions, make the downstream team play the customer role to the upstream team. Negotiate the
budget and tasks for downstream requirements so that everyone understands the commitment and schedule.
CONFORMIST. When two development teams have an upstream / downstream relationship in which the upstream has no
motivation to provide for the downstream team's needs, the downstream team is helpless. Therefore, eliminate the
complexity of translation between bounded contexts by slavishly adhering to the model of the upstream team.
ANTI-CORRUPTION LAYER. When a new system is being built that must have a large interface with another, the difficulty of
relating two models can eventually overwhelm the intent of the new model altogether, causing it to be modified to
resemble the other system's model, in an ad hoc fashion. Therefore, create an isolating layer to provide clients with
functionality in terms of their own domain model. The layer talks to the other system through its existing interface,
requiring little or no modification to the other system.
SEPARATE WAYS. Integration is always expensive. Sometimes the benefit is small. Therefore, declare a bounded context to
have no connection to the others at all, allowing developers to find simple, specialised solutions within this small
scope.
OPEN HOST SERVICE. When a subsystem has to be integrated with many others, there is more and more to maintain and more
and more to worry about when changes are made. Therefore, define a protocol that gives access to your subsystem as a set
of services.
PUBLISHED LANGUAGE. Direct translation to and from the existing domain models may not be a good solution. Those models
may be overly complex or poorly factored. Therefore, use a well-documented shared language that can express the
necessary domain information as a common medium of communication, translating as necessary into and out of that
language.
## Chapter 15: Distillation
CORE DOMAIN. In designing a large system, there are so many contributing components, all complicated and all absolutely
necessary to success, that the essence of the domain model, can be obscured and neglected. Therefore, boil the model
down. Make the core small.
GENERIC SUBDOMAINS. Anything extraneous makes the core domain harder to discern and understand. Therefore, identify
cohesive subdomains that are not the motivation for your project. Factor out generic models of these subdomains and
place them in separate models.
DOMAIN VISION STATEMENT. In later stages of development, there is a need for explanation the value of the system that
does not require an in-depth study of the model. Therefore, write a short description of the core domain. Keep it
narrow. Write this statement early and revise it as you gain new insight.
HIGHLIGHTED CORE. The mental labor of constantly filtering the model to identify key parts absorbs concentration better
spent on design thinking, and it requires comprehensive knowledge of the model. Therefore, write a brief document that
describes the core domain and the primary interactions among core elements.
COHESIVE MECHANISMS. Computations sometimes reach a level of complexity that begins to bloat the design. The
conceptual *what* is swamped by the mechanistic *how*. Therefore, partition a conceptually cohesive mechanism into a
separate lightweight framework.
SEGREGATED CODE. Elements in the model may partially serve the core domain and partially play supporting role. Core
elements may be tightly coupled to generic ones. Therefore, refactor the model to separate the core concepts from
supporting players and strengthen the cohesion of the core while reducing its coupling to other code.
ABSTRACT CORE. When there is a lot of interaction between subdomains in separate modules, either many references will
have to be created between modules, which defeats much of the value of the partitioning or the interaction will have to
be made indirect, which makes the model obscure. Therefore, identify the most fundamental concepts in the model and
factor them into distinct classes, abstract classes or interfaces.
## Chapter 16: Large-Scale Structure
EVOLVING ORDER. Design free-for-all's produce systems no one can make sense of as whole. Therefore, let this conceptual
large-scale structure evolve with the application, possibly changing to a completely different type of structure along
the way. Don't over constrain the detailed design and model decisions that must be made with detailed knowledge.
SYSTEM METAPHOR. Software decisions tend to be very abstract and hard to grasp. Developers and users alike need tangible
ways to understand the system and share a view of the system as a whole. Therefore, organise the design around metaphor
and absorb it into the ubiquitous language.
RESPONSIBILITY LAYERS. When each individual object has handcrafted responsibilities, there are no guidelines, no
infirmity and no ability to handle large swaths of the domain together. Therefore, look at the conceptual dependencies
in your model and the varying rates and sources of change of different parts of your domain. Refactor the model so that
the responsibilities of each domain object fit nearly within the responsibility of one layer.
KNOWLEDGE LEVEL. In application in which the roles and relationships between entities vary in different situations,
complexity can explode. Objects end up with references to other types to cover a variety of cases, or with attributes
that are used in different ways in different situations. Therefore, create a distinct set of objects that can be used to
describe and constrain the structure and behaviour of the basic model.
PLUGGABLE COMPONENT BEHAVIOUR. When a variety of applications have to interoperate, all based on the same abstractions
but designed independently, translations between multiple bounded contexts limit integration. Duplication and
fragmentation raise costs of development and installation. Therefore, distill an abstract core of interfaces and
interactions and create a framework that allow diverse implementations of those interfaces to be freely substituted.
================================================
FILE: books/ddia.md
================================================
[go back](https://github.com/pkardas/learning)
# Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems
Book by Martin Kleppmann
- [Chapter 1: Reliable, Scalable and Maintainable Applications](#chapter-1-reliable-scalable-and-maintainable-applications)
- [Chapter 2: Data Models and Query Languages](#chapter-2-data-models-and-query-languages)
- [Chapter 3: Storage and Retrieval](#chapter-3-storage-and-retrieval)
- [Chapter 4: Encoding and Evolution](#chapter-4-encoding-and-evolution)
- [Chapter 5: Replication](#chapter-5-replication)
- [Chapter 6: Partitioning](#chapter-6-partitioning)
- [Chapter 7: Transactions](#chapter-7-transactions)
- [Chapter 8: The Trouble with Distributed Systems](#chapter-8-the-trouble-with-distributed-systems)
- [Chapter 9: Consistency and Consensus](#chapter-9-consistency-and-consensus)
- [Chapter 10: Batch Processing](#chapter-10-batch-processing)
- [Chapter 11: Stream Processing](#chapter-11-stream-processing)
- [Chapter 12: The Future of Data Systems](#chapter-12-the-future-of-data-systems)
## Chapter 1: Reliable, Scalable and Maintainable Applications
May applications today are data-intensive, CPU is not a problem but amount of data, its complexity and speed of change.
They are built from standard building blocks: database, cache, search index, stream processing, batch processing. These
building blocks have many variants.
*Reliability* - performs as expected, tolerates user's mistakes, good performance, continues to work even if things go
wrong.
Hardware faults - on a cluster with 10 000 disks, you can expect, on average, one disk to die per day. Nowadays,
multi-machine redundancy is no longer required - only in few use cases.
Software errors - e.g. many applications hang simultaneously on 30.06.2012 because of bug in Linux kernel. This kind of
bugs lie dormant for a long time until they are triggered by an unusual set of circumstances.
Human errors - humans are responsible for the majority of errors. There are measures that can be taken in order to
prevent the errors:
- well-defined abstractions, easy to use tools, interfaces that discourage doing the wrong things
- provide fully functional non-production sandbox environment where people can explore and experiment with real data
- test thoroughly at all levels (unit tests, integration, ...)
- provide tools that can recompute the data in case of errors in the past
- set up detailed monitoring
*Scalability* - system's ability to cope with increased load. Load can be described with a few numbers (load parameters)
, e.g. requests per second, read/write ratio, number of simultaneous connections, hit rate on cache or something else.
*Describing performance*
Response times (client waiting time) vary, always look at averages or medians (p50). In order to know how bad you
outliers are you need to look at 95th, 99th and 99.9th percentiles. High percentiles (tail latencies) are important
because they directly affect users' experience. Anyhow, optimising 99.99th percentile might be really expensive.
SLO (service level objectives) and SLA (service level agreements) - contracts that define the expected performance and
availability of a service. Example SLA: service up and median response time < 200 ms, 99th percentile < 1s. High
percentiles are extremely important in backend services that are called multiple times as part of serving a single
end-user request.
*Approaches for coping with load*
Architecture might need to be reworked on every order of magnitude load increase. Because application could handle 2x
bigger load, it doesn't mean it will handle 10x that load.
Scaling up / vertical scaling- moving to more powerful machine. Scaling out / horizontal scaling - distributing the load
across multiple machines.
Distributing stateless services across multiple machines is easy, stateful data systems form a single node to a
distributed setup can introduce a lot of additional complexity. There is no a single, universal approach for all
applications, design is very often highly specific.
*Maintainability*
Design and build systems that will minimise pain during maintenance. Make it easy to understand for new engineers. Allow
for easy changes, adapting for unanticipated use cases as requirements change.
*Simplicity*
Project's complexity grove with time, this slows everyone down. Symptoms of complexity:
- explosion of state space
- tight coupling of modules
- tangled dependencies
- inconsistent naming and terminology
- special-casing to work around issues
Complex software makes it easy to introduce bugs, system makes it harder to understand hidden assumptions, unintended
consequences and many more. **Simplicity should be a key goal for the systems we build**. One of the best tools for
removing complexity is *abstraction*. Great abstraction can hide implementation details behind a clean interface.
*Evolvability*
Requirements change, you learn new facts, new use cases emerge, priorities change, etc. Agile provides a framework for
adapting to change. Modify system and adapt it to changing requirements - pay attention to simplicity and abstractions.
## Chapter 2: Data Models and Query Languages
*Relational Model vs Document Model*. Relational Databases turned out to generalise very well. NoSQL (*Not Only SQL*) is
the latest attempt to overthrow the relational model's dominance.
Driving forces behind NoSQL:
- a need for greater scalability - very large datasets / very high write throughput
- many open source projects
- specialised query operations
- frustration with restrictiveness of relational model
A rule of thumb: is you are duplicating values that could be stored in just one place the schema is not normalised.
Many-to-many relationships are widely used in relational databases, NoSQL reopened the debate on how best to represent
such relationship.
If your data has document-like structure, then it's probably a good idea to use a document model. The relational
database and its shredding (splitting document-like structure into multiple tables) can lead to unnecessary complicated
application code.
Problems with document model: you can not access nested object directly you need to use access path, also it is not
performing well in many-to-many relationships.
Database schema can be compared to languages: relational - compiled language with static typing, document - dynamic (
runtime) type checking - schema on read.
Data locality - because document databases store document as a string continuous string - JSON, XML, ... - often access
will be faster because of locality, if data is split across multiple tables -> multiple disks -> more disk seeks -> more
time required. However, the document database will need to load entire document even if you need a small portion of it.
*Query Languages for Data*
SQL is declarative - you define what you want, and it is up to the computer do determine how to get this data. Most
programming languages are imperative - you define how to process the data.
*MapReduce Querying*
MapReduce - programming model for processing large amounts of data in bulk across many machines. Limited form of
MapReduce is supported by some noSQL data-stores. Something between declarative and imperative programming.
*Graph-Like Data Models*
Very good approach form data with many-to-many relationships. Each vertex has: ID, set of outgoing edges, set of
outgoing edges, a collection of properties (key-value pairs). Each edge has: ID, the tail vertex, the head vertex, label
describing the type of relationship, a collection of properties (key-value pairs).
Graphs give great flexibility in modeling relationships. e.g. France has departments and regions, whereas the US has
counties and states.
Cypher is a declarative query language for property graphs, created for Neo4j DB, e.g.: find the names of all people who
emigrated from the US to Europe:
```cypher
MATCH
(person) -[:BORN_IN]-> () -[:WITHIN*0..]-> (us:Location {name: "United States"}),
(person) -[:LIVES_IN]-> () -[:WITHIN*0..]-> (eu:Location {name: "Europe"})
RETURN person.name
```
This can be expressed in SQL (recursive common table expressions), but with one difficulty, `LIVES_IN` might point to
any location (region, country, state, continent), here we are interested only in the US and Europe. 4 lines in Cypher vs
29 lines in SQL.
*Triple-Stores*
Very similar to graph model, all information stored in the form of very simple three-part
statements: `(subject, predicate, object)`, e.g. `(Jim, likes, bananas)`.
## Chapter 3: Storage and Retrieval
In order to tune a storage engine to perform well on your kind of workload, you need to have a rough idea of what the
storage engine is doing under the hood.
*Data Structures That Power Your Database*
Hash Indexes. For example: Key and offset pairs. SSTable - Sorted String Table.
B-Trees - most widely used indexing structure, standard index implementation for almost all relational databases and for
many non-relational databases. B-trees keep key-value pairs sorted by key, which allows efficient key-value lookups. The
number of references to child pages in one page of the B-tree is called the branching factor. A B-tree with *n* keys
always has depth of *O(log n)*. Most databases can fit into a B-tree that is 3-4 levels deep. 4-level tree of 4KB pages
with a branching factor of 500 can store up to 256TB of data.
In order to make db resilient to crashes, it is common for B-tree implementations to include an additional data
structure on disk - WAL - write-ahead log - append only file, every B-tree modification must be written before it can be
applied on the pages of the tree. When db crashes, this log is used to restore the B-tree to consistent state.
LSM-tree:
- faster for writes
- can be compressed better, thus often produce smaller files on disk
- lower storage overheads
- compaction process can sometimes interfere with the performance of ongoing reads and writes
- if throughput is high and compaction is not configured carefully, compaction might not keep up with the rate of
incoming writes
B-trees are so old, and so well optimised so that they can deliver good, consistent performance for many workloads.
Key-value indexes are for primary key index in the relational model. It is also common to have secondary index. They
don't have to be unique - this can be solved for example by appending row ID.
Clustered index - storing all row data within the index.
Concatenated index - multi-column index, combines several fields into one key by appending one column to another.
What if you search for misspelled data or similar data. Lucene is able to search text for words within a certain edit
distance.
Data structures discussed so far are specific for disks. However, as RAM becomes cheaper and many datasets are feasible
to keep in memory. This led to the development of in-memory databases. Some in-memory key-value stores (Memcached) are
intended for caching, data can be lost on machine restart. Other in-memory databases aim for durability, which can be
achieved with special battery-powered RAM, by writing a log changes to disk or replicating memory state to other
machines. When restarted it needs to load the data from the disk of from a replica. Even though it is an in-memory
database, a disk is still used. Other in-memory databases with relational model: VoltDB, MemSQL, Oracle TimesTen.
RAMCloud is a key-value store, Redis and Couchbase provide weak durability by writing to disk asynchronously.
In-memory databases achieve better performance.
OLTP - Online Transaction Processing - interactive applications - look up fa small number of records, insert or update
records based on user's activity.
OLAP - Online Analytic Processing - second patterns - analytic queries.
In 90s companies stopped using OLTP systems for analytics purposes and shifted to OLAP for running analytics on a
separate database. This separate database is called a data warehouse.
Data warehouse - separate database that analyst can query without affecting OLTP operations. Read-only copy of the data.
Data extracted from OLTP databases, transformed into analysis-friendly schema. Process of getting data info the
warehouse is known as Extract-Transform-Load. Biggest advantage of OLAP for analysis is that this database can be
optimised for large queries.
Many data warehouses use star schema (dimensional modeling). Variation of this schema is called the snowflake schema.
Snowflakes are more normalised than stars.
Fact tables are often 100 columns wide, however `SELECT * ` queries are rarely used. In most OLTP databases, storage is
laid out in a row-oriented fashion. How to execute queries more efficiently? The idea behind column-oriented storage is
simple: don't store all the values from one row together, but store all values from each column together. e.g. one file
= one column - much faster than parsing each row.
Columns can be compressed using for example bitmap encoding - unique values encoded using bits. Efficient in situations
where only few unique values and millions of records. Column compression allows mor rows from a column to fit in L1
cache.
## Chapter 4: Encoding and Evolution
Rolling update / staged rollout - deploying the new version to a few nodes at a time, checking whether the new version
is running smoothly. With client-side applications you are at mercy of the user, who may not install the update for some
time. This means that old and new versions of the code might co-exist for some time.
Backward compatibility - newer code can read data that was written by older code (normally not hard).
Forward compatibility - older code can read data that was written by newer code (this is trickier).
Programs usually work with data in 2 representations:
- in memory - objects, lists, arrays, trees - data structures optimised for efficient access and manipulation by the CPU
- byte sequence structures - for example JSON
The translation from the in-memory to a byte sequence is called encoding. The reverse is called decoding (also: parsing,
deserialization, unmarshalling).
Many programming languages have built-in support for encoding in-memory data structures. Python has pickle, Java has
Serializable, Ruby has Marshal, however:
- encoding is tied to programming language
- potential source of security issues
- Java's built-in serialisation is said to have bad performance
In general, it is bad idea to use language's built-in encoding for anything other than very transient purposes.
JSON:
- built-in support in browsers
- distinguishes strings and numbers
- good support for unicode, no support for binary strings
XML:
- too verbose
- no distinction between numbers and strings
- good support for unicode, no support for binary strings
CSV:
- less powerful than XML and JSON
- no distinction between numbers and strings
- no data schema
Despite flaws of JSON, XML and CSV they are good enough for many purposes, and they will remain popular.
JSON is less verbose than XMAL, but still uses a lot of space - this might be an issue when you are dealing with
terabytes of data. This led to the development of binary encodings for JSON - BSON, BJSON, UBJSON, BISON. XMAL has also
its binary encodings - WBXML and Fast Infoset. However, none of them are widely adopted.
Apache Thrift (Facebook), Protocol Buffers (Google) are binary encoding libraries that are based on the same principle.
Schema defined in interface definition language, this schema can generate code for encoding and decoding data.
Field numbers in Apache Thrift are used for more compact encoding (no need for passing field names through the wire -
CompactProtocol). Required / optional makes no difference for encoding, this is used for the runtime.
Every field you add after the initial deployment of schema must be optional of have default value. Removing is like
adding, you can remove only optional fields. Also, with ProtoBuf / Thrift you can never use the same tag number again.
Avro is another binary encoding format, it has optional code generation for dynamically typed programming languages.
Data can flow through:
- database
- services - REST and RPC, services are similar to databases, they allow clients to submit and query data. A key design
goal of a service-oriented / microservices architecture is to make the application easier to change and maintain by
making services independently deployable and evolvable.
REST is not a protocol, but rather a design philosophy that builds upon the principles of HTTP.
SOAP - XML-based protocol for making network API requests.
RPC - Remote Procedure Call - seems convenient at first, but the approach is fundamentally flawed, because a network
request is very different from a local function call:
- local function is predictable - it can either succeed or fail depending on the input, a network request is
unpredictable - connection might be lost
- a local function call either returns a result or throws an exception, a network request may return without a result -
timeout
- retry mechanism might cause duplication (fist request went through), unless you build deduplication mechanism
- duration of remote call depends on the network congestion
- when you call a local function you can effectively pass references
- if client and server use different languages, data translation might end up ugly
Despite these problems RPC is not going away, modern frameworks are more explicit about the fact that a remote call i
different from local function invocation.
- message passing - something between database passing and services. Similar to RPC because client's request is
delivered with low latency, similar to databases because message is not sent via a direct network connection but goes
through message broker.
Message brokers have a couple of advantages comparing to RPC:
- can acs as a buffer when recipient is unavailable
- can automatically redeliver messages
- avoids the sender to know the IP address and port
- one message can be sent to multiple recipients
- logical decoupling between sender and receiver
## Chapter 5: Replication
Shared-Nothing Architecture - each machine or virtual machine running the database is called a node. Each node uses its
own CPU, RAM and disks independently. Any coordination between nodes is done at the software level using network.
Replication - means keeping a copy of the same data on multiple machines that are connected via a network. Why?
- to reduce latency - copy close to the users
- to allow the system to continue working
- to scale out
If data is not changing, replication is easy, for dealing with replication changes, following algorithms can be used:
single-leader, multi-leader and leaderless replication.
Leaders and Followers - each node (replica) stores a copy of the database . One of the replicas is designated to be a
leader (master), when clients want to write to the database, they must send their requests to the leader. Other replicas
- followers (slaves), take the log from the leader and updates local copy of the data, applying all writes in the same
order as they were processed by the leader. Writes are accepted only to the leader, read can be performed using any
follower.
On follower failure, if the connection between leader and follower is temporarily corrupted, follower can recover
easily, because it knows the last processed transaction from the log. It can request missing data from the last
successful transaction. Leader failure is trickier. One of the followers can be promoted to be the new leader, for
example replica with the most recent data (election) - data loss minimisation.
Implementation of Replication Logs:
- Statement-based replication - leader logs every request (statement) - for relational database this means thet every
INSERT / UPDATE / DELETE statement is forwarded to followers, each follower executes received SQL statement (as if it
had been received from a client)
- Problems - what about NOW, RAND - nondeterministic, what about auto-incrementing fields, what about triggers.
There are some workarounds, like sending request and result or requiring deterministic transactions.
- Write-ahead log (WAL) shipping - log is append-only sequence of bytes containing all writes, this log can be used to
build replica. This method is used in PostgreSQL and Oracle. Disadvantage of this approach is that log contains
low-level information - like which disk blocks were changed, so replication is closely coupled to the storage engine (
or even storage engine version!).
- Logical log replication - alternative approach that uses different log format for replication - decoupling. Usually a
sequence of records describing writes to database tables at the granularity of a row. Easier backward compatibility -
leaders and followers can run different engine versions
- Trigger-based replication - triggers have ability to log changes to a separate table, from which an external process
can read. This allows for replicating for example subset of data.
Problems with replication lag:
- leader-based replication is cool when we need to scale reads, not necessarily writes - common on the web
- synchronous replication - single node failure can make entire system unavailable
- asynchronous replication - follower might fall behind -> inconsistent data (this is temporary situation, if you stop
writing for a while, the followers will catch up and become consistent with the leader - eventual consistency)
Replica lag - anomalies:
- if user writes and then views, the new data might not yet have reached the replica. Read-your-writes consistency -
needed guarantee that if the user reloads the page, whey will always see updates they submitted themselves.
- Solution: owner of the profile views data from the leader, other users view from replica. There are modifications,
for example: if last update older than 1m -> view from replica.
- when reading from asynchronous followers is that user can see things moving back in time - happens when user makes
several reads from different replicas
- Solution: monotonic reads - guarantee stronger than eventual consistency, if user makes several reads in sequence,
they will not see time go backward (never data after older data)
- consistent prefix reads - is a sequence of writes happens in certain order, then anyone reading those writes will see
them appear in the same order
- Solution: make sure that any writes that are casually related to each other are written to the same partition OR
use algorithm that keep track of casual dependencies
When working with an eventual consistent system, it is worth thinking about how the application behaves if the
replication lag increases to several minutes or hours.
Multi-Leader Replication - more than one node accepting writes, each leader simultaneously acts as a follower to the
other leaders. It rarely makes sense to use a multi-leader setup within a single datacenter, because benefits rarely
outweigh the added complexity, however there are some situations in which this configuration makes sense:
- multi-datacenter operation - one leader in each datacenter, multi-leader setup requires conflict resolution mechanism
which can be problematic. Multi-leader replication is often considered dangerous territory that should be avoided if
possible.
- clients with offline operation - for example calendar app have to work even if it is disconnected from the internet,
if you make changes while you are offline then they need to be synced with a server and all other devices. This
basically means every device has a local database that acts as a leader. For example CouchDB is designed for this mode
of operation.
- collaborative editing - multiple people editing the same document - e.g. Google Docs, very similar case to the
previous one. If you want to guarantee that there will be no editing conflicts, the application must obtain a lock on
the document before user can edit - this collaboration model is equivalent to single-leader replication with
transaction on the leader.
Handling Write Conflicts:
- make the conflict detection synchronous - wait for the write to be replicated to all replicas before telling the user
that write was successful
- avoid conflicts - all writes can go through the same leader, requests from particular user are always routed to the
same datacenter and use the leader in that datacenter for writing and reading.
- each replica should converge toward consistent state
- custom conflict resolution - this might depend on the application, code might be executed on write or on read
Automatic Conflict Resolution - Amazon was frequently cited example of surprising effects due to conflict resolution
handler - customers were seeing items removed from the cart. Some ideas for automatic conflict resolution:
- conflict-free replicated datatypes - family of data structures that can be concurrently edited by multiple users
- merge-able persistent data structures - similar to GIT
- operational transformation - algorithm behind Google Docs - designed specifically for concurrent edits of an ordered
list of items - e.g. string.
Replication topology describes the communication paths along which writes are propagated from one node to another (
circular, star, all-to-all).
Leaderless replication - the client sends directly its writes to several replicas, or coordinator node does this on
behalf of the client. When one node is down, some data might be down. For this reason when a client reads from the
database, it sends its requests to multiple replicas and uses data with the most version number. Eventually all the data
will be copied to every replica. 2 approaches with dealing with inconsistent data: whenever client notices inconsistency
or background process looking for differences in the data.
For example in DynamoDB it is possible to set minimum number of replicas that saved the data to mark write as valid.
It is important to monitor replication lag, even if your application can tolerate stale reads.
Dynamo-style databases allow several clients to concurrently write to the same key - this means potential conflicts!
Events may arrive in a different order at different nodes, due to network delays and partial failures (replicas might
store different values). In order to become eventually consistent, the replicas should converge toward the same value.
It is up to the developer to resolve conflict:
- last write wins - discard older values
- detecting happens-before operations (btw. two operations might be considered concurrent when they overlap in time, not
necessarily at the same time)
- merge concurrently written values
- use version vectors - version number per replica and per key, each replica increments its own version number
## Chapter 6: Partitioning
Partitioning - breaking up the data into partitions (each piece of data belongs to exactly one partition). The main
reason for partitioning is scalability - different partitions can be placed on different nodes.
Partitioning is usually combined with replication. Copies of each partition are stored on multiple nodes. The goal with
partitioning is to spread the data and the query load evenly across nodes. If every node takes fair share, e.g. 10 nodes
should be able to handle 10x much data. If partitioning is unfair it is called skewed. Skew makes partitioning less
effective. In order to reduce skew data needs to be distributed evenly.
One way is to assign a continuous range of keys to each partition (PARTITION 1: A-B, PARTITION 2: C-D, ...). The ranges
of keys are not necessarily evenly spaced, for example majority of entries with letter A. Partition boundaries need to
be carefully selected by application developer with domain knowledge. Partitioning by data is problematic too - all
writes going to single partition, whereas remaining partitions are idle. For example, you could solve this issue by
partitioning first by name (for example sensor name) and then by the time, this will balance the load.
Skew can be reduced by using hash function that is evenly distributing data across partitions. The partition boundaries
can be evenly spaced or chosen pseudorandomly (consistent hashing).
Consistent Hashing - a way of evenly distributing load across an internet-wide system of caches such as CDN. Uses
randomly chosen partition boundaries to avoid the need for central control or distributed consensus.
Using hash of the key loses the ability to do efficient range queries (sort order lost)
gitextract_z_5iswqa/
├── .gitignore
├── README.md
├── books/
│ ├── architecture-hard-parts.md
│ ├── build.md
│ ├── clean-agile.md
│ ├── clean-code.md
│ ├── coaching-agile-teams.md
│ ├── code-complete.md
│ ├── comic-agile.md
│ ├── cracking-coding-interview/
│ │ ├── Dockerfile
│ │ ├── docker-compose.yml
│ │ ├── notes.md
│ │ ├── requirements.txt
│ │ └── src/
│ │ ├── ch01_arrays_and_strings/
│ │ │ ├── check_permutation.py
│ │ │ ├── is_unique.py
│ │ │ ├── one_away.py
│ │ │ ├── palindrome_permutation.py
│ │ │ ├── rotate_matrix.py
│ │ │ ├── string_compression.py
│ │ │ ├── string_rotation.py
│ │ │ ├── urlify.py
│ │ │ └── zero_matrix.py
│ │ └── ch02_linked_lists/
│ │ ├── delete_middle_node.py
│ │ ├── intersection.py
│ │ ├── linked_list.py
│ │ ├── loop_detection.py
│ │ ├── palindrome.py
│ │ ├── partition.py
│ │ ├── remove_dups.py
│ │ ├── return_kth_to_last.py
│ │ └── sum_lists.py
│ ├── ddd.md
│ ├── ddia.md
│ ├── docker-deep-dive.md
│ ├── elixir.md
│ ├── fundamentals-of-architecture.md
│ ├── go/
│ │ ├── ch01/
│ │ │ ├── Makefile
│ │ │ └── hello.go
│ │ ├── ch02/
│ │ │ ├── const.go
│ │ │ └── unicode.go
│ │ ├── ch03/
│ │ │ └── types.go
│ │ ├── ch04/
│ │ │ ├── case.go
│ │ │ ├── for.go
│ │ │ └── if.go
│ │ ├── ch05/
│ │ │ ├── anonymous.go
│ │ │ ├── deferExample.go
│ │ │ ├── functionAsParam.go
│ │ │ ├── functions.go
│ │ │ ├── functionsAreValues.go
│ │ │ └── returnFunction.go
│ │ ├── ch06/
│ │ │ └── pointers.go
│ │ ├── ch07/
│ │ │ ├── counter.go
│ │ │ ├── dependencyInjection.go
│ │ │ ├── embedding.go
│ │ │ ├── intTree.go
│ │ │ ├── interfaces.go
│ │ │ ├── iota.go
│ │ │ └── types.go
│ │ ├── ch08/
│ │ │ ├── customErrors.go
│ │ │ ├── errors.go
│ │ │ ├── panic.go
│ │ │ ├── recover.go
│ │ │ ├── sentinel.go
│ │ │ └── wrappingErrors.go
│ │ ├── ch09/
│ │ │ ├── formatter/
│ │ │ │ └── formatter.go
│ │ │ ├── main.go
│ │ │ └── math/
│ │ │ └── math.go
│ │ ├── ch10/
│ │ │ ├── deadlock.go
│ │ │ ├── deadlockSolution.go
│ │ │ └── goroutinesExample.go
│ │ └── notes.md
│ ├── hands-on-ml.md
│ ├── head-first-design-patterns/
│ │ ├── ch_01_strategy.py
│ │ ├── ch_02_observer.py
│ │ ├── ch_03_decorator.py
│ │ ├── ch_04_factory.py
│ │ ├── ch_05_singleton.py
│ │ ├── ch_06_command.py
│ │ ├── ch_07_adapter.py
│ │ ├── ch_07_facade.py
│ │ ├── ch_08_template_method.py
│ │ ├── ch_09_composite.py
│ │ ├── ch_09_iterator.py
│ │ ├── ch_10_state.py
│ │ ├── ch_11_virtual_proxy.py
│ │ └── notes.md
│ ├── kubernetes-book.md
│ ├── kubernetes-in-action.md
│ ├── nlp-book.md
│ ├── peopleware.md
│ ├── pragmatic-programmer.md
│ ├── pytest/
│ │ ├── .coveragerc
│ │ ├── Dockerfile
│ │ ├── docker-compose.yml
│ │ ├── notes.md
│ │ ├── requirements.txt
│ │ ├── setup.cfg
│ │ ├── src/
│ │ │ ├── __init__.py
│ │ │ ├── api.py
│ │ │ ├── cli.py
│ │ │ └── db.py
│ │ └── tests/
│ │ ├── ch_02/
│ │ │ ├── test_card.py
│ │ │ ├── test_classes.py
│ │ │ ├── test_exceptions.py
│ │ │ └── test_helper.py
│ │ ├── ch_03/
│ │ │ ├── conftest.py
│ │ │ ├── test_autouse.py
│ │ │ ├── test_count.py
│ │ │ ├── test_count_initial.py
│ │ │ ├── test_fixtures.py
│ │ │ ├── test_rename_fixture.py
│ │ │ └── test_some.py
│ │ ├── ch_04/
│ │ │ ├── conftest.py
│ │ │ ├── test_config.py
│ │ │ ├── test_tmp.py
│ │ │ └── test_version.py
│ │ ├── ch_05/
│ │ │ └── test_parametrize.py
│ │ ├── ch_06/
│ │ │ ├── pytest.ini
│ │ │ ├── test_builtin.py
│ │ │ ├── test_custom.py
│ │ │ └── text_combination.py
│ │ ├── ch_12/
│ │ │ ├── hello.py
│ │ │ └── test_hello.py
│ │ └── ch_15/
│ │ ├── conftest.py
│ │ ├── pytest.ini
│ │ └── test_slow.py
│ ├── python-architecture-patterns/
│ │ ├── Dockerfile
│ │ ├── Makefile
│ │ ├── docker-compose.yml
│ │ ├── notes.md
│ │ ├── requirements.txt
│ │ ├── setup.cfg
│ │ ├── src/
│ │ │ ├── __init__.py
│ │ │ ├── adapters/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── notifications.py
│ │ │ │ ├── orm.py
│ │ │ │ ├── redis_publisher.py
│ │ │ │ └── repository.py
│ │ │ ├── app.py
│ │ │ ├── bootstrap.py
│ │ │ ├── config.py
│ │ │ ├── domain/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── commands.py
│ │ │ │ ├── events.py
│ │ │ │ └── model.py
│ │ │ ├── redis_consumer.py
│ │ │ ├── service_layer/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── handlers.py
│ │ │ │ ├── message_bus.py
│ │ │ │ └── unit_of_work.py
│ │ │ └── views.py
│ │ └── tests/
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── e2e/
│ │ │ ├── __init__.py
│ │ │ ├── api_client.py
│ │ │ ├── redis_client.py
│ │ │ ├── test_app.py
│ │ │ └── test_external_events.py
│ │ ├── integration/
│ │ │ ├── __init__.py
│ │ │ ├── test_uow.py
│ │ │ └── test_views.py
│ │ └── unit/
│ │ ├── __init__.py
│ │ ├── test_batches.py
│ │ ├── test_handlers.py
│ │ └── test_product.py
│ ├── refactoring.md
│ ├── release-it.md
│ ├── system-design-interview.md
│ ├── tidy-first.md
│ └── understanding-distributed-systems.md
├── case-studies/
│ └── reddit.md
├── conferences/
│ ├── aws-innovate-ai-ml-21.md
│ ├── brown-bags.md
│ └── pycon-2022.md
├── courses/
│ └── fast-ai.md
├── patterns/
│ ├── abbreviations.md
│ └── architecture.md
└── teaching/
├── python-intermediate/
│ └── README.md
└── python-intro/
├── README.md
└── notebook.ipynb
SYMBOL INDEX (693 symbols across 115 files)
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/check_permutation.py
function check_permutation_sets (line 4) | def check_permutation_sets(string: str, potential_permutation_string: st...
function check_permutation_sort (line 8) | def check_permutation_sort(string: str, potential_permutation_string: st...
function check_permutation_array (line 12) | def check_permutation_array(string: str, potential_permutation_string: s...
function test_algorithm (line 45) | def test_algorithm(function, string, potential_permutation_string, is_pe...
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/is_unique.py
function check_if_has_unique_characters_pythonic (line 4) | def check_if_has_unique_characters_pythonic(string: str) -> bool:
function check_if_has_unique_characters_ascii (line 8) | def check_if_has_unique_characters_ascii(string: str) -> bool:
function check_if_has_unique_characters_no_structures (line 18) | def check_if_has_unique_characters_no_structures(string: str) -> bool:
function check_if_has_unique_characters_no_structures_sort (line 26) | def check_if_has_unique_characters_no_structures_sort(string: str) -> bool:
function test_algorithm (line 50) | def test_algorithm(function, string, has_all_unique_chars):
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/one_away.py
function is_one_edit_away_pythonic (line 4) | def is_one_edit_away_pythonic(string: str, edit: str) -> bool:
function is_one_edit_away_loop (line 14) | def is_one_edit_away_loop(string: str, edit: str) -> bool:
function test_algorithm (line 55) | def test_algorithm(function, string, edit, expected_result):
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/palindrome_permutation.py
function is_palindrome_permutation_pythonic (line 6) | def is_palindrome_permutation_pythonic(string: str) -> bool:
function is_palindrome_permutation_counter (line 16) | def is_palindrome_permutation_counter(string: str) -> bool:
function test_algorithm (line 48) | def test_algorithm(function, string, expected_result):
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/rotate_matrix.py
function rotate_matrix_list_comprehension (line 6) | def rotate_matrix_list_comprehension(matrix: List[List[int]]) -> List[Li...
function rotate_matrix_zip (line 14) | def rotate_matrix_zip(matrix: List[List[int]]) -> List[List[int]]:
function test_algorithm (line 42) | def test_algorithm(function, matrix, rotated_matrix):
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/string_compression.py
function compress_string (line 6) | def compress_string(text: str) -> str:
function test_algorithm (line 32) | def test_algorithm(text, expected_result):
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/string_rotation.py
function is_rotated (line 4) | def is_rotated(string: str, rotated_string: str) -> bool:
function test_algorithm (line 18) | def test_algorithm(string, rotated_string, expected_result):
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/urlify.py
function urlify_pythonic (line 4) | def urlify_pythonic(url: str) -> str:
function urlify_array (line 8) | def urlify_array(url: str) -> str:
function test_algorithm (line 45) | def test_algorithm(function, url, expected_url):
FILE: books/cracking-coding-interview/src/ch01_arrays_and_strings/zero_matrix.py
function nullify_loop (line 6) | def nullify_loop(matrix: List[List[int]]) -> List[List[int]]:
function nullify_in_place (line 25) | def nullify_in_place(matrix: List[List[int]]) -> List[List[int]]:
function test_algorithm (line 65) | def test_algorithm(function, matrix, rotated_matrix):
FILE: books/cracking-coding-interview/src/ch02_linked_lists/delete_middle_node.py
function delete_middle_node (line 9) | def delete_middle_node(node: Node) -> None:
function test_algorithm (line 22) | def test_algorithm(values, node, expected_result):
FILE: books/cracking-coding-interview/src/ch02_linked_lists/intersection.py
function intersection (line 11) | def intersection(list_0: LinkedList, list_1: LinkedList) -> Optional[Node]:
function test_algorithm (line 50) | def test_algorithm(list_0, list_0_tail, list_1, list_1_tail, expected_re...
FILE: books/cracking-coding-interview/src/ch02_linked_lists/linked_list.py
class Node (line 9) | class Node:
method __init__ (line 10) | def __init__(self, data: int) -> None:
class LinkedList (line 15) | class LinkedList:
method __init__ (line 16) | def __init__(self, data: List[int]) -> None:
method values (line 22) | def values(self) -> List[int]:
method tail (line 30) | def tail(self) -> Optional[Node]:
method length (line 37) | def length(self) -> int:
method node_for_value (line 40) | def node_for_value(self, val: int) -> Optional[Node]:
method append (line 48) | def append(self, data: int) -> None:
method delete (line 51) | def delete(self, data: int) -> None:
function delete (line 55) | def delete(head: Optional[Node], data: int) -> Optional[Node]:
function append (line 73) | def append(head: Optional[Node], data: int) -> Optional[Node]:
function test_append (line 91) | def test_append(values):
function test_delete (line 105) | def test_delete(values, to_delete, expected_result):
function test_node_for_value (line 117) | def test_node_for_value(values, value, expected_node_val):
function test_tail (line 129) | def test_tail(values, expected_tail):
FILE: books/cracking-coding-interview/src/ch02_linked_lists/loop_detection.py
function get_loop (line 11) | def get_loop(linked_list: LinkedList) -> Optional[Node]:
function test_algorithm (line 46) | def test_algorithm(linked_list, expected_result):
FILE: books/cracking-coding-interview/src/ch02_linked_lists/palindrome.py
function is_palindrome_simple (line 9) | def is_palindrome_simple(linked_list: LinkedList) -> bool:
function is_palindrome_reverse (line 14) | def is_palindrome_reverse(linked_list: LinkedList) -> bool:
function is_palindrome_slow_fast_runner (line 39) | def is_palindrome_slow_fast_runner(linked_list: LinkedList) -> bool:
function test_algorithm (line 74) | def test_algorithm(function, values, expected_result):
FILE: books/cracking-coding-interview/src/ch02_linked_lists/partition.py
function partition (line 7) | def partition(linked_list: LinkedList, partition_val: int) -> Tuple[Link...
function test_algorithm (line 28) | def test_algorithm(values, partition_val, expected_values):
FILE: books/cracking-coding-interview/src/ch02_linked_lists/remove_dups.py
function remove_duplicates_buffer (line 6) | def remove_duplicates_buffer(linked_list: LinkedList) -> LinkedList:
function remove_duplicates_no_buffer (line 21) | def remove_duplicates_no_buffer(linked_list: LinkedList) -> LinkedList:
function test_algorithm (line 52) | def test_algorithm(function, values, expected_result):
FILE: books/cracking-coding-interview/src/ch02_linked_lists/return_kth_to_last.py
function return_kth_to_last_simple (line 11) | def return_kth_to_last_simple(linked_list: LinkedList, k: int) -> int:
function return_kth_to_last_simplest (line 25) | def return_kth_to_last_simplest(linked_list: LinkedList, k: int) -> int:
function return_kth_to_last_recursive (line 32) | def return_kth_to_last_recursive(linked_list: LinkedList, k: int) -> int:
function return_kth_to_last_iterative (line 52) | def return_kth_to_last_iterative(linked_list: LinkedList, k: int) -> int:
function test_algorithm (line 81) | def test_algorithm(function, values, k, expected_result):
FILE: books/cracking-coding-interview/src/ch02_linked_lists/sum_lists.py
function sum_lists (line 9) | def sum_lists(list_0: LinkedList, list_1: LinkedList) -> LinkedList:
function test_algorithm (line 46) | def test_algorithm(list_0, list_1, expected_result):
FILE: books/go/ch01/hello.go
function main (line 5) | func main() {
FILE: books/go/ch02/const.go
constant x (line 5) | x int64 = 10
constant idKey (line 8) | idKey = "id"
constant nameKey (line 9) | nameKey = "name"
constant z (line 12) | z = 20 * 20
function main (line 14) | func main() {
FILE: books/go/ch02/unicode.go
function main (line 5) | func main() {
FILE: books/go/ch03/types.go
function main (line 5) | func main() {
FILE: books/go/ch04/case.go
function main (line 5) | func main() {
FILE: books/go/ch04/for.go
function main (line 7) | func main() {
function completeFor (line 15) | func completeFor() {
function conditionOnlyFor (line 21) | func conditionOnlyFor() {
function infiniteFor (line 29) | func infiniteFor() {
function forRange (line 36) | func forRange() {
function labelingStatements (line 56) | func labelingStatements() {
FILE: books/go/ch04/if.go
function main (line 8) | func main() {
FILE: books/go/ch05/anonymous.go
function main (line 5) | func main() {
FILE: books/go/ch05/deferExample.go
function getFile (line 9) | func getFile(name string) (*os.File, func(), error) {
function main (line 19) | func main() {
FILE: books/go/ch05/functionAsParam.go
type Person (line 8) | type Person struct
function main (line 14) | func main() {
FILE: books/go/ch05/functions.go
function main (line 8) | func main() {
function Div (line 27) | func Div(numerator int, denominator int) int {
type MyFuncOpts (line 34) | type MyFuncOpts struct
function MyFunc (line 40) | func MyFunc(opts MyFuncOpts) int {
function addTo (line 44) | func addTo(base int, vals ...int) []int {
function divAndRemainder (line 52) | func divAndRemainder(numerator int, denominator int) (result int, remain...
FILE: books/go/ch05/functionsAreValues.go
function main (line 5) | func main() {
function add (line 16) | func add(i int, j int) int { return i + j }
function sub (line 17) | func sub(i int, j int) int { return i - j }
function mul (line 18) | func mul(i int, j int) int { return i * j }
function div (line 19) | func div(i int, j int) int { return i / j }
FILE: books/go/ch05/returnFunction.go
function makeMult (line 5) | func makeMult(base int) func(int) int {
function main (line 11) | func main() {
FILE: books/go/ch06/pointers.go
function failedUpdate (line 5) | func failedUpdate(px *int) {
function update (line 10) | func update(px *int) {
function main (line 14) | func main() {
FILE: books/go/ch07/counter.go
type Counter (line 8) | type Counter struct
method Increment (line 13) | func (c *Counter) Increment() {
method String (line 18) | func (c Counter) String() string {
function updateWrong (line 22) | func updateWrong(c Counter) {
function updateRight (line 27) | func updateRight(c *Counter) {
function main (line 32) | func main() {
FILE: books/go/ch07/dependencyInjection.go
function LogOutput (line 9) | func LogOutput(message string) {
type SimpleDataStore (line 13) | type SimpleDataStore struct
method UserNameForId (line 17) | func (sds SimpleDataStore) UserNameForId(userID string) (string, bool) {
function NewSimpleDataStore (line 22) | func NewSimpleDataStore() SimpleDataStore {
type DataStore (line 32) | type DataStore interface
type Logger (line 36) | type Logger interface
type LoggerAdapter (line 40) | type LoggerAdapter
method Log (line 42) | func (lg LoggerAdapter) Log(message string) {
type SimpleLogic (line 46) | type SimpleLogic struct
method SayHello (line 51) | func (sl SimpleLogic) SayHello(userID string) (string, error) {
method SayGoodbye (line 60) | func (sl SimpleLogic) SayGoodbye(userID string) (string, error) {
function NewSimpleLogic (line 69) | func NewSimpleLogic(l Logger, ds DataStore) SimpleLogic {
type MyLogic (line 76) | type MyLogic interface
type Controller (line 80) | type Controller struct
method SayHello (line 85) | func (c Controller) SayHello(w http.ResponseWriter, r *http.Request) {
function NewController (line 97) | func NewController(l Logger, logic MyLogic) Controller {
function main (line 104) | func main() {
FILE: books/go/ch07/embedding.go
type Employee (line 5) | type Employee struct
method Description (line 10) | func (e Employee) Description() string {
type Manager (line 14) | type Manager struct
function main (line 19) | func main() {
FILE: books/go/ch07/intTree.go
type IntTree (line 5) | type IntTree struct
method Insert (line 10) | func (it *IntTree) Insert(val int) *IntTree {
method Contains (line 24) | func (it *IntTree) Contains(val int) bool {
function main (line 37) | func main() {
FILE: books/go/ch07/interfaces.go
type LogicProvider (line 5) | type LogicProvider struct
method Process (line 7) | func (lp LogicProvider) Process(data string) string {
type Logic (line 11) | type Logic interface
type Client (line 15) | type Client struct
method Program (line 19) | func (c Client) Program() {
function main (line 24) | func main() {
FILE: books/go/ch07/iota.go
type MailCategory (line 3) | type MailCategory
constant Uncategorized (line 6) | Uncategorized MailCategory = iota
constant Personal (line 7) | Personal
constant Spam (line 8) | Spam
constant Social (line 9) | Social
constant Ads (line 10) | Ads
FILE: books/go/ch07/types.go
type Person (line 5) | type Person struct
method String (line 13) | func (p Person) String() string {
type King (line 11) | type King
type Score (line 17) | type Score
type Converter (line 18) | type Converter
type TeamScore (line 19) | type TeamScore
function main (line 21) | func main() {
FILE: books/go/ch08/customErrors.go
type Status (line 3) | type Status
constant InvalidLogin (line 6) | InvalidLogin Status = iota + 1
constant NotFound (line 7) | NotFound
type StatusErr (line 10) | type StatusErr struct
method Error (line 16) | func (se StatusErr) Error() string {
method Unwrap (line 20) | func (se StatusErr) Unwrap() error {
FILE: books/go/ch08/errors.go
function calcRemainderAndMod (line 9) | func calcRemainderAndMod(numerator, denominator int) (int, int, error) {
function main (line 16) | func main() {
FILE: books/go/ch08/panic.go
function doPanic (line 3) | func doPanic(msg string) {
function main (line 7) | func main() {
FILE: books/go/ch08/recover.go
function div60 (line 5) | func div60(i int) {
function main (line 14) | func main() {
FILE: books/go/ch08/sentinel.go
type Sentinel (line 9) | type Sentinel
method Error (line 11) | func (s Sentinel) Error() string {
constant ErrFoo (line 16) | ErrFoo = Sentinel("foo err")
constant ErrBar (line 17) | ErrBar = Sentinel("bar err")
function main (line 20) | func main() {
FILE: books/go/ch08/wrappingErrors.go
function fileChecker (line 9) | func fileChecker(name string) error {
function main (line 19) | func main() {
FILE: books/go/ch09/formatter/formatter.go
function Format (line 5) | func Format(num int) string {
FILE: books/go/ch09/main.go
function main (line 9) | func main() {
FILE: books/go/ch09/math/math.go
function Double (line 3) | func Double(a int) int {
FILE: books/go/ch10/deadlock.go
function main (line 5) | func main() {
FILE: books/go/ch10/deadlockSolution.go
function main (line 5) | func main() {
FILE: books/go/ch10/goroutinesExample.go
function process (line 3) | func process(val int) int {
function runThingConcurrently (line 7) | func runThingConcurrently(in <-chan int, out chan<- int) {
FILE: books/head-first-design-patterns/ch_01_strategy.py
class FlyBehavior (line 1) | class FlyBehavior:
method fly (line 2) | def fly(self) -> None:
class QuackBehavior (line 6) | class QuackBehavior:
method quack (line 7) | def quack(self) -> None:
class Duck (line 11) | class Duck:
method __init__ (line 12) | def __init__(self, fly_behavior: FlyBehavior, quack_behavior: QuackBeh...
method perform_fly (line 16) | def perform_fly(self) -> None:
method perform_quack (line 19) | def perform_quack(self) -> None:
method display (line 22) | def display(self) -> None:
class FlyWithWings (line 26) | class FlyWithWings(FlyBehavior):
method fly (line 27) | def fly(self) -> None:
class FlyNoWay (line 31) | class FlyNoWay(FlyBehavior):
method fly (line 32) | def fly(self) -> None:
class Quack (line 36) | class Quack(QuackBehavior):
method quack (line 37) | def quack(self) -> None:
class Squeak (line 41) | class Squeak(QuackBehavior):
method quack (line 42) | def quack(self) -> None:
class MuteQuack (line 46) | class MuteQuack(QuackBehavior):
method quack (line 47) | def quack(self) -> None:
class MallardDuck (line 51) | class MallardDuck(Duck):
method __init__ (line 52) | def __init__(self) -> None:
method display (line 55) | def display(self) -> None:
FILE: books/head-first-design-patterns/ch_02_observer.py
class Observer (line 1) | class Observer:
method update (line 2) | def update(self) -> None:
class Subject (line 6) | class Subject:
method register_observer (line 7) | def register_observer(self, observer: Observer) -> None:
method remove_observer (line 10) | def remove_observer(self, observer: Observer) -> None:
method notify_observers (line 13) | def notify_observers(self) -> None:
class DisplayElement (line 17) | class DisplayElement:
method display (line 18) | def display(self) -> None:
class WeatherData (line 22) | class WeatherData(Subject):
method __init__ (line 23) | def __init__(self):
method register_observer (line 29) | def register_observer(self, observer: Observer) -> None:
method remove_observer (line 32) | def remove_observer(self, observer: Observer) -> None:
method notify_observers (line 35) | def notify_observers(self) -> None:
method set_measurements (line 39) | def set_measurements(self, temperature: float, humidity: float, pressu...
class CurrentConditionsDisplay (line 46) | class CurrentConditionsDisplay(Observer, DisplayElement):
method __init__ (line 47) | def __init__(self, weather_data: WeatherData):
method display (line 53) | def display(self) -> None:
method update (line 56) | def update(self) -> None:
class AvgTempDisplay (line 62) | class AvgTempDisplay(Observer, DisplayElement):
method __init__ (line 63) | def __init__(self, weather_data: WeatherData):
method display (line 68) | def display(self) -> None:
method update (line 71) | def update(self) -> None:
FILE: books/head-first-design-patterns/ch_03_decorator.py
class Beverage (line 1) | class Beverage:
method description (line 3) | def description(self) -> str:
method cost (line 7) | def cost(self) -> float:
class CondimentDecorator (line 11) | class CondimentDecorator(Beverage):
method __init__ (line 12) | def __init__(self, beverage: Beverage):
method description (line 16) | def description(self) -> str:
method cost (line 20) | def cost(self) -> float:
class Espresso (line 24) | class Espresso(Beverage):
method cost (line 26) | def cost(self) -> float:
class HouseBlend (line 30) | class HouseBlend(Beverage):
method cost (line 32) | def cost(self) -> float:
class Mocha (line 36) | class Mocha(CondimentDecorator):
method cost (line 38) | def cost(self) -> float:
class Soy (line 42) | class Soy(CondimentDecorator):
method cost (line 44) | def cost(self) -> float:
FILE: books/head-first-design-patterns/ch_04_factory.py
class Ingredient (line 1) | class Ingredient:
method __init__ (line 2) | def __init__(self):
class ThinCrustDough (line 6) | class ThinCrustDough(Ingredient):
class ThickCrustDough (line 10) | class ThickCrustDough(Ingredient):
class MarinaraSauce (line 14) | class MarinaraSauce(Ingredient):
class PlumTomatoSauce (line 18) | class PlumTomatoSauce(Ingredient):
class MozzarellaCheese (line 22) | class MozzarellaCheese(Ingredient):
class ReggianoCheese (line 26) | class ReggianoCheese(Ingredient):
class Garlic (line 30) | class Garlic(Ingredient):
class Onion (line 34) | class Onion(Ingredient):
class Mushroom (line 38) | class Mushroom(Ingredient):
class SlicedPepperoni (line 42) | class SlicedPepperoni(Ingredient):
class FreshClams (line 46) | class FreshClams(Ingredient):
class FrozenClams (line 50) | class FrozenClams(Ingredient):
class PizzaIngredientFactory (line 54) | class PizzaIngredientFactory:
method create_dough (line 55) | def create_dough(self):
method create_sauce (line 58) | def create_sauce(self):
method create_cheese (line 61) | def create_cheese(self):
method create_veggies (line 64) | def create_veggies(self):
method create_pepperoni (line 67) | def create_pepperoni(self):
method create_clam (line 70) | def create_clam(self):
class NYPizzaIngredientFactory (line 74) | class NYPizzaIngredientFactory(PizzaIngredientFactory):
method create_dough (line 75) | def create_dough(self):
method create_sauce (line 78) | def create_sauce(self):
method create_cheese (line 81) | def create_cheese(self):
method create_veggies (line 84) | def create_veggies(self):
method create_pepperoni (line 87) | def create_pepperoni(self):
method create_clam (line 90) | def create_clam(self):
class ChicagoPizzaIngredientFactory (line 94) | class ChicagoPizzaIngredientFactory(PizzaIngredientFactory):
method create_dough (line 95) | def create_dough(self):
method create_sauce (line 98) | def create_sauce(self):
method create_cheese (line 101) | def create_cheese(self):
method create_veggies (line 104) | def create_veggies(self):
method create_pepperoni (line 107) | def create_pepperoni(self):
method create_clam (line 110) | def create_clam(self):
class Pizza (line 114) | class Pizza:
method __init__ (line 117) | def __init__(self, ingredient_factory: PizzaIngredientFactory):
method prepare (line 120) | def prepare(self) -> None:
method bake (line 123) | def bake(self) -> None:
method cut (line 126) | def cut(self) -> None:
method box (line 129) | def box(self) -> None:
class CheesePizza (line 133) | class CheesePizza(Pizza):
method prepare (line 134) | def prepare(self) -> None:
class ClamPizza (line 141) | class ClamPizza(Pizza):
method prepare (line 142) | def prepare(self) -> None:
class PizzaStore (line 150) | class PizzaStore:
method order_pizza (line 151) | def order_pizza(self, pizza_type: str) -> Pizza:
method create_pizza (line 162) | def create_pizza(self, pizza_type: str) -> Pizza:
class NYPizzaStore (line 166) | class NYPizzaStore(PizzaStore):
method create_pizza (line 167) | def create_pizza(self, pizza_type: str) -> Pizza:
class ChicagoPizzaStore (line 183) | class ChicagoPizzaStore(PizzaStore):
method create_pizza (line 184) | def create_pizza(self, pizza_type: str) -> Pizza:
FILE: books/head-first-design-patterns/ch_05_singleton.py
class ChocolateBoiler (line 1) | class ChocolateBoiler:
method __new__ (line 4) | def __new__(cls):
class ChocolateBoiler (line 19) | class ChocolateBoiler:
method __new__ (line 4) | def __new__(cls):
function get_chocolate_boiler (line 28) | def get_chocolate_boiler() -> ChocolateBoiler:
function get_chocolate_boiler (line 40) | def get_chocolate_boiler() -> ChocolateBoiler:
FILE: books/head-first-design-patterns/ch_06_command.py
class Device (line 4) | class Device:
method name (line 6) | def name(self) -> str:
method on (line 9) | def on(self) -> None:
method off (line 12) | def off(self) -> None:
class Light (line 16) | class Light(Device):
class Tv (line 20) | class Tv(Device):
class Stereo (line 24) | class Stereo(Device):
method __init__ (line 25) | def __init__(self) -> None:
method set_cd (line 28) | def set_cd(self) -> None:
method set_volume (line 31) | def set_volume(self, volume: int) -> None:
class Command (line 36) | class Command:
method execute (line 37) | def execute(self) -> None:
method undo (line 40) | def undo(self) -> None:
class NoCommand (line 44) | class NoCommand(Command):
method execute (line 45) | def execute(self) -> None:
method undo (line 48) | def undo(self) -> None:
class MarcoCommand (line 52) | class MarcoCommand(Command):
method __init__ (line 53) | def __init__(self, commands: List[Command]):
method execute (line 56) | def execute(self) -> None:
method undo (line 60) | def undo(self) -> None:
class DeviceOnCommand (line 65) | class DeviceOnCommand(Command):
method __init__ (line 66) | def __init__(self, device: Device) -> None:
method execute (line 69) | def execute(self) -> None:
method undo (line 72) | def undo(self) -> None:
class DeviceOffCommand (line 76) | class DeviceOffCommand(Command):
method __init__ (line 77) | def __init__(self, device: Device) -> None:
method execute (line 80) | def execute(self) -> None:
method undo (line 83) | def undo(self) -> None:
class StereoVolumeUpCommand (line 87) | class StereoVolumeUpCommand(Command):
method __init__ (line 88) | def __init__(self, stereo: Stereo) -> None:
method execute (line 91) | def execute(self) -> None:
method undo (line 94) | def undo(self) -> None:
class RemoteControl (line 98) | class RemoteControl:
method __init__ (line 99) | def __init__(self):
method set_command (line 104) | def set_command(self, slot: int, on_command: Command, off_command: Com...
method on_button_pushed (line 108) | def on_button_pushed(self, slot: int) -> None:
method off_button_pushed (line 112) | def off_button_pushed(self, slot: int) -> None:
method undo_button_pushed (line 116) | def undo_button_pushed(self) -> None:
FILE: books/head-first-design-patterns/ch_07_adapter.py
class Duck (line 1) | class Duck:
method quack (line 2) | def quack(self) -> None:
method fly (line 5) | def fly(self) -> None:
class Turkey (line 9) | class Turkey:
method gobble (line 10) | def gobble(self) -> None:
method fly (line 13) | def fly(self) -> None:
class WildTurkey (line 17) | class WildTurkey(Turkey):
method gobble (line 18) | def gobble(self) -> None:
method fly (line 21) | def fly(self) -> None:
class TurkeyAdapter (line 25) | class TurkeyAdapter(Duck):
method __init__ (line 26) | def __init__(self, turkey: Turkey):
method quack (line 29) | def quack(self) -> None:
method fly (line 32) | def fly(self) -> None:
FILE: books/head-first-design-patterns/ch_07_facade.py
class HomeTheaterFacade (line 4) | class HomeTheaterFacade:
method __init__ (line 5) | def __init__(self, amplifier, tuner, projector, lights, screen, player...
method watch_movie (line 15) | def watch_movie(self, movie):
FILE: books/head-first-design-patterns/ch_08_template_method.py
class CaffeineBeverage (line 1) | class CaffeineBeverage:
method prepare_recipe (line 2) | def prepare_recipe(self) -> None:
method _boil_water (line 8) | def _boil_water(self) -> None:
method _pour_in_cup (line 11) | def _pour_in_cup(self) -> None:
method _brew (line 14) | def _brew(self) -> None:
method _add_condiments (line 17) | def _add_condiments(self) -> None:
class Tea (line 21) | class Tea(CaffeineBeverage):
method _brew (line 22) | def _brew(self) -> None:
method _add_condiments (line 25) | def _add_condiments(self) -> None:
class Coffee (line 29) | class Coffee(CaffeineBeverage):
method _brew (line 30) | def _brew(self) -> None:
method _add_condiments (line 33) | def _add_condiments(self) -> None:
FILE: books/head-first-design-patterns/ch_09_composite.py
class MenuComponent (line 7) | class MenuComponent:
method add (line 8) | def add(self, menu_component: MenuComponent):
method remove (line 11) | def remove(self, menu_component: MenuComponent):
method get_child (line 14) | def get_child(self, i: int):
method print (line 17) | def print(self):
class MenuItem (line 22) | class MenuItem(MenuComponent, ABC):
method print (line 28) | def print(self):
class Menu (line 32) | class Menu(MenuComponent):
method __init__ (line 33) | def __init__(self, name: str):
method add (line 37) | def add(self, menu_component: MenuComponent):
method remove (line 40) | def remove(self, menu_component: MenuComponent):
method get_child (line 43) | def get_child(self, i: int):
method print (line 46) | def print(self):
class Waitress (line 52) | class Waitress:
method __init__ (line 53) | def __init__(self, menu_component: MenuComponent):
method print_menu (line 56) | def print_menu(self):
FILE: books/head-first-design-patterns/ch_09_iterator.py
class MenuItem (line 11) | class MenuItem:
class DinnerMenuIterator (line 18) | class DinnerMenuIterator(Iterator):
method __init__ (line 20) | def __init__(self, collection: List[MenuItem]):
method __next__ (line 24) | def __next__(self) -> MenuItem:
class DinnerMenu (line 34) | class DinnerMenu:
method __iter__ (line 43) | def __iter__(self) -> DinnerMenuIterator:
class BreakfastMenuIterator (line 48) | class BreakfastMenuIterator(Iterator):
method __init__ (line 50) | def __init__(self, collection: Dict[str, MenuItem]):
method __next__ (line 54) | def __next__(self) -> MenuItem:
class BreakfastMenu (line 64) | class BreakfastMenu:
method __iter__ (line 72) | def __iter__(self) -> BreakfastMenuIterator:
class Waitress (line 77) | class Waitress:
method __init__ (line 78) | def __init__(self, pancake_menu: BreakfastMenu, dinner_menu: DinnerMenu):
method print_menu (line 82) | def print_menu(self):
method _print_menu (line 89) | def _print_menu(menu: Union[BreakfastMenu, DinnerMenu]):
FILE: books/head-first-design-patterns/ch_10_state.py
class State (line 6) | class State:
method __init__ (line 7) | def __init__(self, gumball_machine: GumballMachine):
method insert_quarter (line 10) | def insert_quarter(self) -> None:
method eject_quarter (line 13) | def eject_quarter(self) -> None:
method turn_crank (line 16) | def turn_crank(self) -> None:
method dispense (line 19) | def dispense(self) -> None:
class NoQuarterState (line 23) | class NoQuarterState(State):
method insert_quarter (line 24) | def insert_quarter(self) -> None:
class HasQuarterState (line 29) | class HasQuarterState(State):
method eject_quarter (line 30) | def eject_quarter(self) -> None:
method turn_crank (line 34) | def turn_crank(self) -> None:
class SoldState (line 43) | class SoldState(State):
method dispense (line 44) | def dispense(self) -> None:
class SoldOutState (line 54) | class SoldOutState(State):
class WinnerState (line 58) | class WinnerState(State):
method dispense (line 59) | def dispense(self) -> None:
class GumballMachine (line 75) | class GumballMachine:
method __init__ (line 76) | def __init__(self, count: int):
method insert_quarter (line 87) | def insert_quarter(self) -> None:
method eject_quarter (line 90) | def eject_quarter(self) -> None:
method turn_crank (line 93) | def turn_crank(self) -> None:
method release_ball (line 97) | def release_ball(self) -> None:
FILE: books/head-first-design-patterns/ch_11_virtual_proxy.py
class Icon (line 1) | class Icon:
method width (line 3) | def width(self) -> int:
method height (line 7) | def height(self) -> int:
method paint_icon (line 10) | def paint_icon(self) -> None:
class ImageIcon (line 14) | class ImageIcon(Icon):
method width (line 16) | def width(self) -> int:
method height (line 20) | def height(self) -> int:
method paint_icon (line 23) | def paint_icon(self) -> None:
class ImageProxy (line 27) | class ImageProxy(Icon):
method __init__ (line 28) | def __init__(self, url: str):
method width (line 34) | def width(self) -> int:
method height (line 38) | def height(self) -> int:
method paint_icon (line 41) | def paint_icon(self) -> None:
FILE: books/pytest/src/api.py
class Card (line 22) | class Card:
method from_dict (line 29) | def from_dict(cls, d):
method to_dict (line 32) | def to_dict(self):
class CardsException (line 36) | class CardsException(Exception):
class MissingSummary (line 40) | class MissingSummary(CardsException):
class InvalidCardId (line 44) | class InvalidCardId(CardsException):
class CardsDB (line 48) | class CardsDB:
method __init__ (line 49) | def __init__(self, db_path):
method add_card (line 53) | def add_card(self, card: Card) -> int:
method get_card (line 63) | def get_card(self, card_id: int) -> Card:
method list_cards (line 71) | def list_cards(self, owner=None, state=None):
function count (line 91) | def count(self) -> int:
function update_card (line 95) | def update_card(self, card_id: int, card_mods: Card) -> None:
function start (line 102) | def start(self, card_id: int):
function finish (line 106) | def finish(self, card_id: int):
function delete_card (line 110) | def delete_card(self, card_id: int) -> None:
function delete_all (line 117) | def delete_all(self) -> None:
function close (line 121) | def close(self):
function path (line 124) | def path(self):
FILE: books/pytest/src/cli.py
function version (line 18) | def version():
function add (line 24) | def add(
function delete (line 34) | def delete(card_id: int):
function list_cards (line 44) | def list_cards(
function update (line 67) | def update(
function start (line 84) | def start(card_id: int):
function finish (line 94) | def finish(card_id: int):
function config (line 104) | def config():
function count (line 111) | def count():
function main (line 118) | def main(ctx: typer.Context):
function get_path (line 126) | def get_path():
function cards_db (line 136) | def cards_db():
FILE: books/pytest/src/db.py
class DB (line 7) | class DB:
method __init__ (line 8) | def __init__(self, db_path, db_file_prefix):
method create (line 13) | def create(self, item: dict) -> int:
method read (line 17) | def read(self, id: int):
method read_all (line 21) | def read_all(self):
method update (line 24) | def update(self, id: int, mods) -> None:
method delete (line 28) | def delete(self, id: int) -> None:
method delete_all (line 31) | def delete_all(self) -> None:
method count (line 34) | def count(self) -> int:
method close (line 37) | def close(self):
FILE: books/pytest/tests/ch_02/test_card.py
function test_field_access (line 6) | def test_field_access():
function test_defaults (line 11) | def test_defaults():
function test_equality (line 16) | def test_equality():
function test_equality_with_different_ids (line 20) | def test_equality_with_different_ids():
function test_inequality (line 24) | def test_inequality():
function test_to_dict (line 28) | def test_to_dict():
function test_from_dict (line 37) | def test_from_dict():
FILE: books/pytest/tests/ch_02/test_classes.py
class TestEquality (line 4) | class TestEquality:
method test_equality (line 5) | def test_equality(self):
method test_equality_with_different_ids (line 8) | def test_equality_with_different_ids(self):
method test_inequality (line 11) | def test_inequality(self):
FILE: books/pytest/tests/ch_02/test_exceptions.py
function test_no_path_raises (line 5) | def test_no_path_raises():
function test_raises_with_info (line 10) | def test_raises_with_info():
FILE: books/pytest/tests/ch_02/test_helper.py
function assert_identical (line 6) | def assert_identical(c1: Card, c2: Card):
function test_identical (line 15) | def test_identical():
function test_identical_fail (line 20) | def test_identical_fail():
FILE: books/pytest/tests/ch_03/conftest.py
function db (line 13) | def db():
function cards_db (line 22) | def cards_db(db):
function some_cards (line 28) | def some_cards():
FILE: books/pytest/tests/ch_03/test_autouse.py
function non_empty_db (line 12) | def non_empty_db(cards_db, some_cards):
function footer_session_scope (line 19) | def footer_session_scope():
function footer_function_scope (line 28) | def footer_function_scope():
function test_1 (line 35) | def test_1():
function test_2 (line 39) | def test_2():
FILE: books/pytest/tests/ch_03/test_count.py
function test_empty (line 4) | def test_empty(cards_db):
function test_two (line 8) | def test_two(cards_db):
function test_three (line 14) | def test_three(cards_db):
FILE: books/pytest/tests/ch_03/test_count_initial.py
function test_empty (line 7) | def test_empty():
FILE: books/pytest/tests/ch_03/test_fixtures.py
function some_data (line 5) | def some_data():
function test_some_data (line 9) | def test_some_data(some_data):
FILE: books/pytest/tests/ch_03/test_rename_fixture.py
function ultimate_answer_fixture (line 5) | def ultimate_answer_fixture():
function test_everything (line 9) | def test_everything(ultimate_answer):
FILE: books/pytest/tests/ch_03/test_some.py
function non_empty_db (line 5) | def non_empty_db(cards_db, some_cards):
function test_add_some (line 11) | def test_add_some(cards_db, some_cards):
function test_non_empty (line 18) | def test_non_empty(non_empty_db):
FILE: books/pytest/tests/ch_04/conftest.py
function db (line 6) | def db(tmp_path_factory):
FILE: books/pytest/tests/ch_04/test_config.py
function run_cards (line 5) | def run_cards(*params):
function test_run_cards (line 11) | def test_run_cards():
function test_patch_get_path (line 15) | def test_patch_get_path(monkeypatch, tmp_path):
function test_patch_home (line 23) | def test_patch_home(monkeypatch, tmp_path):
function test_patch_env_var (line 33) | def test_patch_env_var(monkeypatch, tmp_path):
FILE: books/pytest/tests/ch_04/test_tmp.py
function test_tmp_path (line 1) | def test_tmp_path(tmp_path):
function test_tmp_path_factory (line 7) | def test_tmp_path_factory(tmp_path_factory):
FILE: books/pytest/tests/ch_04/test_version.py
function test_version (line 6) | def test_version(capsys):
function test_version_v2 (line 12) | def test_version_v2():
FILE: books/pytest/tests/ch_05/test_parametrize.py
function db (line 10) | def db(tmp_path_factory):
function cards_db (line 18) | def cards_db(db):
function test_finish (line 24) | def test_finish(cards_db, initial_state):
function start_state (line 35) | def start_state(request):
function test_finish_v2 (line 39) | def test_finish_v2(cards_db, start_state):
function pytest_generate_tests (line 49) | def pytest_generate_tests(metafunc):
function test_finish_v3 (line 54) | def test_finish_v3(cards_db, start_state_2):
FILE: books/pytest/tests/ch_06/test_builtin.py
function test_less_than_skip (line 15) | def test_less_than_skip():
function test_less_than_skipif (line 23) | def test_less_than_skipif():
function test_less_than_xfail (line 31) | def test_less_than_xfail():
function test_xpass (line 36) | def test_xpass():
function test_xpass_strict (line 42) | def test_xpass_strict():
FILE: books/pytest/tests/ch_06/test_custom.py
function db (line 12) | def db(tmp_path_factory):
function cards_db (line 20) | def cards_db(db):
function test_start (line 26) | def test_start(cards_db):
function test_start_non_existent (line 34) | def test_start_non_existent(cards_db):
FILE: books/pytest/tests/ch_06/text_combination.py
function db (line 9) | def db(tmp_path_factory):
function cards_db (line 17) | def cards_db(db, request, faker):
function test_zero (line 30) | def test_zero(cards_db):
function test_three (line 35) | def test_three(cards_db):
FILE: books/pytest/tests/ch_12/hello.py
function main (line 1) | def main():
FILE: books/pytest/tests/ch_12/test_hello.py
function test_hello (line 4) | def test_hello(capsys):
FILE: books/pytest/tests/ch_15/conftest.py
function pytest_configure (line 4) | def pytest_configure(config):
function pytest_addoption (line 8) | def pytest_addoption(parser):
function pytest_collection_modifyitems (line 12) | def pytest_collection_modifyitems(config, items):
FILE: books/pytest/tests/ch_15/test_slow.py
function test_normal (line 4) | def test_normal():
function test_slow (line 8) | def test_slow():
FILE: books/python-architecture-patterns/src/adapters/notifications.py
class AbstractNotifications (line 13) | class AbstractNotifications(ABC):
method send (line 15) | def send(self, destination, message):
class EmailNotifications (line 19) | class EmailNotifications(AbstractNotifications):
method __init__ (line 20) | def __init__(self, smtp_host=DEFAULT_HOST, port=DEFAULT_PORT):
method send (line 24) | def send(self, destination, message):
FILE: books/python-architecture-patterns/src/adapters/orm.py
class AllocationsView (line 7) | class AllocationsView(SQLModel, table=True):
function create_db_and_tables (line 14) | def create_db_and_tables(engine):
function clean_db_and_tables (line 18) | def clean_db_and_tables(engine):
FILE: books/python-architecture-patterns/src/adapters/redis_publisher.py
function publish (line 9) | def publish(channel: str, event: Event):
FILE: books/python-architecture-patterns/src/adapters/repository.py
class AbstractRepository (line 18) | class AbstractRepository(Protocol):
method add (line 19) | def add(self, product: Product):
method get (line 22) | def get(self, sku: str) -> Optional[Product]:
method get_by_batch_ref (line 25) | def get_by_batch_ref(self, ref: str) -> Optional[Product]:
class Repository (line 29) | class Repository(AbstractRepository):
method __init__ (line 30) | def __init__(self, session: Session):
method add (line 33) | def add(self, product: Product):
method get (line 37) | def get(self, sku: str) -> Optional[Product]:
method get_by_batch_ref (line 40) | def get_by_batch_ref(self, ref: str) -> Optional[Product]:
class TrackingRepository (line 44) | class TrackingRepository(AbstractRepository):
method __init__ (line 47) | def __init__(self, repo: AbstractRepository):
method add (line 52) | def add(self, product: Product):
method get (line 56) | def get(self, sku: str) -> Optional[Product]:
method get_by_batch_ref (line 62) | def get_by_batch_ref(self, ref: str) -> Optional[Product]:
FILE: books/python-architecture-patterns/src/app.py
function allocate_endpoint (line 22) | async def allocate_endpoint(order_line: OrderLine, response: Response):
function add_batch_endpoint (line 33) | async def add_batch_endpoint(batch: Batch):
function allocate_view_endpoint (line 39) | async def allocate_view_endpoint(order_id: str, response: Response):
FILE: books/python-architecture-patterns/src/bootstrap.py
function bootstrap (line 25) | def bootstrap(start_orm: bool = True, engine: Engine = create_engine(con...
function inject_dependencies (line 46) | def inject_dependencies(handler, dependencies):
FILE: books/python-architecture-patterns/src/config.py
function get_postgres_uri (line 4) | def get_postgres_uri():
function get_api_url (line 12) | def get_api_url():
function get_redis_host_and_port (line 18) | def get_redis_host_and_port():
function get_email_host_and_port (line 24) | def get_email_host_and_port():
FILE: books/python-architecture-patterns/src/domain/commands.py
class Command (line 6) | class Command:
class Allocate (line 11) | class Allocate(Command):
class CreateBatch (line 18) | class CreateBatch(Command):
class ChangeBatchQuantity (line 26) | class ChangeBatchQuantity(Command):
FILE: books/python-architecture-patterns/src/domain/events.py
class Event (line 4) | class Event(BaseModel):
class OutOfStock (line 8) | class OutOfStock(Event):
class Allocated (line 12) | class Allocated(Event):
class Deallocated (line 19) | class Deallocated(Event):
class BatchQuantityChanged (line 25) | class BatchQuantityChanged(Event):
FILE: books/python-architecture-patterns/src/domain/model.py
class OutOfStock (line 26) | class OutOfStock(Exception):
class OrderLine (line 30) | class OrderLine(SQLModel, table=True):
class Batch (line 40) | class Batch(SQLModel, table=True):
method __eq__ (line 51) | def __eq__(self, other):
method __hash__ (line 56) | def __hash__(self):
method __gt__ (line 59) | def __gt__(self, other):
method allocate (line 66) | def allocate(self, order_line: OrderLine) -> None:
method deallocate (line 73) | def deallocate(self, order_line: OrderLine) -> None:
method deallocate_one (line 78) | def deallocate_one(self):
method allocated_quantity (line 82) | def allocated_quantity(self) -> int:
method available_quantity (line 86) | def available_quantity(self) -> int:
method can_allocate (line 89) | def can_allocate(self, order_line: OrderLine) -> bool:
class Product (line 93) | class Product(SQLModel, table=True):
method __hash__ (line 102) | def __hash__(self):
method messages (line 106) | def messages(self) -> List[Message]:
method allocate (line 109) | def allocate(self, order_line: OrderLine) -> Optional[str]:
method change_batch_quantity (line 125) | def change_batch_quantity(self, ref: str, qty: int):
FILE: books/python-architecture-patterns/src/redis_consumer.py
function main (line 17) | def main():
function _handle_change_batch_quantity (line 26) | def _handle_change_batch_quantity(message: Dict, bus: MessageBus):
FILE: books/python-architecture-patterns/src/service_layer/handlers.py
class InvalidSku (line 19) | class InvalidSku(Exception):
function allocate (line 23) | def allocate(command: commands.Allocate, uow: AbstractUnitOfWork) -> str:
function add_batch (line 37) | def add_batch(command: commands.CreateBatch, uow: AbstractUnitOfWork):
function change_batch_quantity (line 47) | def change_batch_quantity(command: commands.ChangeBatchQuantity, uow: Ab...
function send_out_of_stock_notification (line 54) | def send_out_of_stock_notification(event: events.OutOfStock, notificatio...
function publish_allocated_event (line 58) | def publish_allocated_event(event: events.Allocated, uow: AbstractUnitOf...
function add_allocation_to_read_model (line 62) | def add_allocation_to_read_model(event: events.Allocated, uow: UnitOfWork):
function remove_allocation_from_read_model (line 74) | def remove_allocation_from_read_model(event: events.Deallocated, uow: Un...
function reallocate (line 86) | def reallocate(event: events.Deallocated, uow: AbstractUnitOfWork, ):
FILE: books/python-architecture-patterns/src/service_layer/message_bus.py
class MessageBus (line 34) | class MessageBus:
method __init__ (line 35) | def __init__(self, uow: AbstractUnitOfWork, event_handlers: Dict[Type[...
method handle (line 42) | def handle(self, message: Message):
method _handle_event (line 53) | def _handle_event(self, event: events.Event):
method _handle_command (line 63) | def _handle_command(self, command: commands.Command):
FILE: books/python-architecture-patterns/src/service_layer/unit_of_work.py
class AbstractUnitOfWork (line 21) | class AbstractUnitOfWork(ABC):
method __enter__ (line 24) | def __enter__(self) -> AbstractUnitOfWork:
method __exit__ (line 27) | def __exit__(self, *args):
method commit (line 30) | def commit(self):
method collect_new_messages (line 33) | def collect_new_messages(self):
method rollback (line 39) | def rollback(self):
method _commit (line 43) | def _commit(self):
function default_session (line 47) | def default_session():
class UnitOfWork (line 51) | class UnitOfWork(AbstractUnitOfWork):
method __init__ (line 52) | def __init__(self, session: Optional[Session] = None):
method __enter__ (line 56) | def __enter__(self):
method __exit__ (line 60) | def __exit__(self, *args):
method rollback (line 64) | def rollback(self):
method _commit (line 67) | def _commit(self):
FILE: books/python-architecture-patterns/src/views.py
function allocations (line 9) | def allocations(order_id: str, uow: UnitOfWork) -> List[Dict]:
FILE: books/python-architecture-patterns/tests/conftest.py
function in_memory_db (line 22) | def in_memory_db():
function session (line 30) | def session(in_memory_db):
function wait_for_postgres_to_come_up (line 37) | def wait_for_postgres_to_come_up(engine):
function wait_for_redis_to_come_up (line 42) | def wait_for_redis_to_come_up():
function postgres_db (line 48) | def postgres_db():
function postgres_session (line 57) | def postgres_session(postgres_db):
function client (line 64) | def client():
FILE: books/python-architecture-patterns/tests/e2e/api_client.py
function post_to_allocate (line 9) | def post_to_allocate(client, order_id, sku, qty):
function get_allocation (line 13) | def get_allocation(client, order_id):
function post_to_add_batch (line 17) | def post_to_add_batch(client, ref, sku, qty, eta):
FILE: books/python-architecture-patterns/tests/e2e/redis_client.py
function subscribe_to (line 9) | def subscribe_to(channel):
function publish_message (line 17) | def publish_message(channel, message):
FILE: books/python-architecture-patterns/tests/e2e/test_app.py
function random_suffix (line 11) | def random_suffix():
function random_sku (line 15) | def random_sku(name=''):
function random_batch_ref (line 19) | def random_batch_ref(name=''):
function random_order_id (line 23) | def random_order_id(name=''):
function test_happy_path_returns_200_and_allocated_batch (line 27) | def test_happy_path_returns_200_and_allocated_batch(client):
function test_unhappy_path_returns_400_and_error_message (line 43) | def test_unhappy_path_returns_400_and_error_message(client):
FILE: books/python-architecture-patterns/tests/e2e/test_external_events.py
function test_change_batch_quantity_leading_to_allocation (line 22) | def test_change_batch_quantity_leading_to_allocation(client):
FILE: books/python-architecture-patterns/tests/integration/test_uow.py
function insert_batch (line 23) | def insert_batch(session, batch_id):
function get_allocated_batch_ref (line 27) | def get_allocated_batch_ref(session, order_id, sku):
function test_uow_retrieve_batch_and_allocate_to_it (line 33) | def test_uow_retrieve_batch_and_allocate_to_it(session):
function test_rolls_back_uncommitted_work_by_default (line 46) | def test_rolls_back_uncommitted_work_by_default(in_memory_db):
function test_rolls_back_on_error (line 53) | def test_rolls_back_on_error(in_memory_db):
function try_to_allocate (line 67) | def try_to_allocate(order_id: str, exceptions: List[Exception]):
function test_concurrent_updates_to_version_number_are_not_allowed (line 79) | def test_concurrent_updates_to_version_number_are_not_allowed(postgres_db):
FILE: books/python-architecture-patterns/tests/integration/test_views.py
function sqlite_bus (line 17) | def sqlite_bus(in_memory_db):
function test_allocations_view (line 28) | def test_allocations_view(sqlite_bus):
function test_deallocation (line 44) | def test_deallocation(sqlite_bus):
FILE: books/python-architecture-patterns/tests/unit/test_batches.py
function batch_and_line (line 9) | def batch_and_line(sku, batch_quantity, line_quantity):
function test_allocating_to_batch_reduces_available_quantity (line 13) | def test_allocating_to_batch_reduces_available_quantity():
function test_can_allocate_if_available_greater_than_required (line 19) | def test_can_allocate_if_available_greater_than_required():
function test_cannot_allocate_if_available_smaller_than_required (line 24) | def test_cannot_allocate_if_available_smaller_than_required():
function test_not_allocate_if_available_equal_to_required (line 29) | def test_not_allocate_if_available_equal_to_required():
function test_cannot_allocate_if_skus_dont_match (line 34) | def test_cannot_allocate_if_skus_dont_match():
function test_can_only_deallocate_allocated_lines (line 40) | def test_can_only_deallocate_allocated_lines():
function test_allocation_is_idempotent (line 46) | def test_allocation_is_idempotent():
FILE: books/python-architecture-patterns/tests/unit/test_handlers.py
class FakeRepository (line 25) | class FakeRepository(AbstractRepository):
method __init__ (line 26) | def __init__(self, products):
method add (line 30) | def add(self, product: Product):
method get (line 33) | def get(self, sku: str) -> Optional[Product]:
method get_by_batch_ref (line 36) | def get_by_batch_ref(self, ref: str) -> Optional[Product]:
class FakeUnitOfWork (line 40) | class FakeUnitOfWork(AbstractUnitOfWork):
method __init__ (line 41) | def __init__(self):
method rollback (line 45) | def rollback(self):
method _commit (line 48) | def _commit(self):
class FakeNotifications (line 52) | class FakeNotifications(AbstractNotifications):
method __init__ (line 53) | def __init__(self):
method send (line 56) | def send(self, destination, message):
function bootstrap_test_app (line 60) | def bootstrap_test_app():
class TestAddBatch (line 69) | class TestAddBatch:
method test_for_new_product (line 70) | def test_for_new_product(self):
method test_for_existing_product (line 76) | def test_for_existing_product(self):
class TestAllocate (line 83) | class TestAllocate:
method test_errors_for_invalid_sku (line 84) | def test_errors_for_invalid_sku(self):
method test_commits (line 90) | def test_commits(self):
method test_sends_email_on_out_of_stock_error (line 96) | def test_sends_email_on_out_of_stock_error(self):
class TestChangeBatchQuantity (line 109) | class TestChangeBatchQuantity:
method test_changes_available_quantity (line 110) | def test_changes_available_quantity(self):
method test_reallocates_if_necessary (line 120) | def test_reallocates_if_necessary(self):
FILE: books/python-architecture-patterns/tests/unit/test_product.py
function test_prefers_current_stock_batches_to_shipments (line 11) | def test_prefers_current_stock_batches_to_shipments():
function test_prefers_earlier_batches (line 23) | def test_prefers_earlier_batches():
function test_returns_allocated_batch_ref (line 37) | def test_returns_allocated_batch_ref():
function test_records_out_of_stock_event_if_cannot_allocate (line 48) | def test_records_out_of_stock_event_if_cannot_allocate():
Condensed preview — 180 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (963K chars).
[
{
"path": ".gitignore",
"chars": 111,
"preview": ".DS_Store\n.AppleDouble\n.LSOverride\n.idea\n.ipynb_checkpoints\n*/.pytest_cache/\ngit-user.sh\n/excluded_resources/*\n"
},
{
"path": "README.md",
"chars": 3066,
"preview": "# 👉👉👉 Visit [musicat.fm](https://musicat.fm) 😻\n\nYou can connect Spotify and Apple Music to it to discover many cool stat"
},
{
"path": "books/architecture-hard-parts.md",
"chars": 212,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Software Architecture: The Hard Parts: Modern Tradeoff Analysis for Di"
},
{
"path": "books/build.md",
"chars": 15174,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Build\n\nBook by Tony Fadell\n\n- [1.1 Adulthood](#11-adulthood)\n- [1.2 Ge"
},
{
"path": "books/clean-agile.md",
"chars": 39349,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Clean Agile: Back to Basics\n\nBook by Robert Cecil Martin\n\n- [Chapter 1"
},
{
"path": "books/clean-code.md",
"chars": 15451,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Clean Code: A Handbook of Agile Software Craftsmanship\n\nBook by Robert"
},
{
"path": "books/coaching-agile-teams.md",
"chars": 1802,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Coaching Agile Teams\n\nBook by Lyssa Adkins\n\n- [1. Will I be a Good Coa"
},
{
"path": "books/code-complete.md",
"chars": 23081,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Code Complete: A Practical Handbook of Software Construction\n\nBook by "
},
{
"path": "books/comic-agile.md",
"chars": 3363,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Comic Agilé\n\nBook by Luxshan Ratnaravi, Mikkel Noe-Nygaard\n\n- [1: Tran"
},
{
"path": "books/cracking-coding-interview/Dockerfile",
"chars": 147,
"preview": "FROM python:3.10.4\n\nWORKDIR /src\n\nENV PYTHONPATH \"${PYTHONPATH}:/src\"\n\nCOPY requirements.txt .\nRUN pip install -r requir"
},
{
"path": "books/cracking-coding-interview/docker-compose.yml",
"chars": 124,
"preview": "version: \"3.9\"\nservices:\n interview:\n build:\n context: .\n dockerfile: Dockerfile\n volumes:\n - ./:/"
},
{
"path": "books/cracking-coding-interview/notes.md",
"chars": 9092,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Cracking the Coding Interview: 189 Programming Questions and Solutions"
},
{
"path": "books/cracking-coding-interview/requirements.txt",
"chars": 14,
"preview": "pytest==7.1.2\n"
},
{
"path": "books/cracking-coding-interview/src/ch01_arrays_and_strings/check_permutation.py",
"chars": 1534,
"preview": "import pytest\n\n\ndef check_permutation_sets(string: str, potential_permutation_string: str) -> bool:\n return len(strin"
},
{
"path": "books/cracking-coding-interview/src/ch01_arrays_and_strings/is_unique.py",
"chars": 1398,
"preview": "import pytest\n\n\ndef check_if_has_unique_characters_pythonic(string: str) -> bool:\n return len(set(string)) == len(str"
},
{
"path": "books/cracking-coding-interview/src/ch01_arrays_and_strings/one_away.py",
"chars": 1438,
"preview": "import pytest\n\n\ndef is_one_edit_away_pythonic(string: str, edit: str) -> bool:\n if abs(len(string) - len(edit)) > 1:\n"
},
{
"path": "books/cracking-coding-interview/src/ch01_arrays_and_strings/palindrome_permutation.py",
"chars": 1314,
"preview": "from collections import Counter\n\nimport pytest\n\n\ndef is_palindrome_permutation_pythonic(string: str) -> bool:\n raw_st"
},
{
"path": "books/cracking-coding-interview/src/ch01_arrays_and_strings/rotate_matrix.py",
"chars": 968,
"preview": "from typing import List\n\nimport pytest\n\n\ndef rotate_matrix_list_comprehension(matrix: List[List[int]]) -> List[List[int]"
},
{
"path": "books/cracking-coding-interview/src/ch01_arrays_and_strings/string_compression.py",
"chars": 789,
"preview": "from dataclasses import dataclass\n\nimport pytest\n\n\ndef compress_string(text: str) -> str:\n @dataclass\n class Compr"
},
{
"path": "books/cracking-coding-interview/src/ch01_arrays_and_strings/string_rotation.py",
"chars": 650,
"preview": "import pytest\n\n\ndef is_rotated(string: str, rotated_string: str) -> bool:\n return len(string) == len(rotated_string) "
},
{
"path": "books/cracking-coding-interview/src/ch01_arrays_and_strings/urlify.py",
"chars": 1236,
"preview": "import pytest\n\n\ndef urlify_pythonic(url: str) -> str:\n return ' '.join(url.split()).replace(' ', \"%20\")\n\n\ndef urlify_"
},
{
"path": "books/cracking-coding-interview/src/ch01_arrays_and_strings/zero_matrix.py",
"chars": 1523,
"preview": "from typing import List\n\nimport pytest\n\n\ndef nullify_loop(matrix: List[List[int]]) -> List[List[int]]:\n height, width"
},
{
"path": "books/cracking-coding-interview/src/ch02_linked_lists/delete_middle_node.py",
"chars": 615,
"preview": "import pytest\n\nfrom linked_list import (\n LinkedList,\n Node,\n)\n\n\ndef delete_middle_node(node: Node) -> None:\n a"
},
{
"path": "books/cracking-coding-interview/src/ch02_linked_lists/intersection.py",
"chars": 1362,
"preview": "from typing import Optional\n\nimport pytest\n\nfrom linked_list import (\n LinkedList,\n Node,\n)\n\n\ndef intersection(lis"
},
{
"path": "books/cracking-coding-interview/src/ch02_linked_lists/linked_list.py",
"chars": 2942,
"preview": "from typing import (\n List,\n Optional,\n)\n\nimport pytest\n\n\nclass Node:\n def __init__(self, data: int) -> None:\n "
},
{
"path": "books/cracking-coding-interview/src/ch02_linked_lists/loop_detection.py",
"chars": 940,
"preview": "from typing import Optional\n\nimport pytest\n\nfrom linked_list import (\n LinkedList,\n Node,\n)\n\n\ndef get_loop(linked_"
},
{
"path": "books/cracking-coding-interview/src/ch02_linked_lists/palindrome.py",
"chars": 1790,
"preview": "import pytest\n\nfrom linked_list import (\n LinkedList,\n Node,\n)\n\n\ndef is_palindrome_simple(linked_list: LinkedList)"
},
{
"path": "books/cracking-coding-interview/src/ch02_linked_lists/partition.py",
"chars": 914,
"preview": "from typing import Tuple\n\nimport pytest\nfrom linked_list import LinkedList\n\n\ndef partition(linked_list: LinkedList, part"
},
{
"path": "books/cracking-coding-interview/src/ch02_linked_lists/remove_dups.py",
"chars": 1327,
"preview": "import pytest\n\nfrom linked_list import LinkedList\n\n\ndef remove_duplicates_buffer(linked_list: LinkedList) -> LinkedList:"
},
{
"path": "books/cracking-coding-interview/src/ch02_linked_lists/return_kth_to_last.py",
"chars": 1832,
"preview": "from typing import Optional\n\nimport pytest\n\nfrom linked_list import (\n LinkedList,\n Node,\n)\n\n\ndef return_kth_to_la"
},
{
"path": "books/cracking-coding-interview/src/ch02_linked_lists/sum_lists.py",
"chars": 1453,
"preview": "import pytest\n\nfrom linked_list import (\n LinkedList,\n Node,\n)\n\n\ndef sum_lists(list_0: LinkedList, list_1: LinkedL"
},
{
"path": "books/ddd.md",
"chars": 31075,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Domain-Driven Design: Tackling Complexity in the Heart of Software\n\nBo"
},
{
"path": "books/ddia.md",
"chars": 67063,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Designing Data-Intensive Applications: The Big Ideas Behind Reliable, "
},
{
"path": "books/docker-deep-dive.md",
"chars": 89,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Docker Deep Dive\n\nBook by Nigel Poulton"
},
{
"path": "books/elixir.md",
"chars": 87,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Elixir in Action\n\nBook by Saša Jurić\n"
},
{
"path": "books/fundamentals-of-architecture.md",
"chars": 78061,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Fundamentals of Software Architecture: An Engineering Approach.\n\nBook "
},
{
"path": "books/go/ch01/Makefile",
"chars": 391,
"preview": "# Set default target, when 'make' executed, runs 'build' by default:\n.DEFAULT_GOAL := build\n\nfmt:\n\tgo fmt ./...\n# Keep '"
},
{
"path": "books/go/ch01/hello.go",
"chars": 74,
"preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello, world!\")\n}\n"
},
{
"path": "books/go/ch02/const.go",
"chars": 258,
"preview": "package main\n\nimport \"fmt\"\n\nconst x int64 = 10\n\nconst (\n\tidKey = \"id\"\n\tnameKey = \"name\"\n)\n\nconst z = 20 * 20\n\nfunc mai"
},
{
"path": "books/go/ch02/unicode.go",
"chars": 82,
"preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tęąćśż := \"hello\"\n\tfmt.Println(ęąćśż)\n}\n"
},
{
"path": "books/go/ch03/types.go",
"chars": 1191,
"preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar x [3]int\n\tfmt.Println(x)\n\n\tvar y = [12]int{1, 5: 4}\n\tfmt.Println(y)\n\n\tvar"
},
{
"path": "books/go/ch04/case.go",
"chars": 397,
"preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\twords := []string{\"a\", \"cow\", \"smile\", \"gopher\"}\n\n\tfor _, word := range words"
},
{
"path": "books/go/ch04/for.go",
"chars": 933,
"preview": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tcompleteFor()\n\tconditionOnlyFor()\n\tinfiniteFor()\n\tforRange()\n\tlabelingSt"
},
{
"path": "books/go/ch04/if.go",
"chars": 238,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nfunc main() {\n\tif n := rand.Intn(10); n == 10 {\n\t\tfmt.Println(\"That's too "
},
{
"path": "books/go/ch05/anonymous.go",
"chars": 166,
"preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 0; i < 5; i++ {\n\t\tfunc(j int) {\n\t\t\tfmt.Println(\"printing\", j, \"from "
},
{
"path": "books/go/ch05/deferExample.go",
"chars": 553,
"preview": "package main\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"os\"\n)\n\nfunc getFile(name string) (*os.File, func(), error) {\n\tf, err := os.Open(na"
},
{
"path": "books/go/ch05/functionAsParam.go",
"chars": 359,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\ntype Person struct {\n\tFirstName string\n\tLastName string\n\tAge int\n}\n\nfunc"
},
{
"path": "books/go/ch05/functions.go",
"chars": 1080,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tresult := Div(5, 2)\n\tfmt.Println(result)\n\n\tMyFunc(MyFuncOpts{\n"
},
{
"path": "books/go/ch05/functionsAreValues.go",
"chars": 350,
"preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar opMap = map[string]func(int, int) int{\n\t\t\"+\": add,\n\t\t\"-\": sub,\n\t\t\"*\": mul"
},
{
"path": "books/go/ch05/returnFunction.go",
"chars": 264,
"preview": "package main\n\nimport \"fmt\"\n\nfunc makeMult(base int) func(int) int {\n\treturn func(factor int) int {\n\t\treturn base * facto"
},
{
"path": "books/go/ch06/pointers.go",
"chars": 243,
"preview": "package main\n\nimport \"fmt\"\n\nfunc failedUpdate(px *int) {\n\tx2 := 20\n\tpx = &x2\n}\n\nfunc update(px *int) {\n\t*px = 20\n}\n\nfunc"
},
{
"path": "books/go/ch07/counter.go",
"chars": 681,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype Counter struct {\n\ttotal int\n\tlastUpdated time.Time\n}\n\nfunc (c *Count"
},
{
"path": "books/go/ch07/dependencyInjection.go",
"chars": 2062,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc LogOutput(message string) {\n\tfmt.Println(message)\n}\n\ntype Si"
},
{
"path": "books/go/ch07/embedding.go",
"chars": 401,
"preview": "package main\n\nimport \"fmt\"\n\ntype Employee struct {\n\tName string\n\tID string\n}\n\nfunc (e Employee) Description() string {"
},
{
"path": "books/go/ch07/intTree.go",
"chars": 741,
"preview": "package main\n\nimport \"log\"\n\ntype IntTree struct {\n\tval int\n\tleft, right *IntTree\n}\n\nfunc (it *IntTree) Insert(va"
},
{
"path": "books/go/ch07/interfaces.go",
"chars": 399,
"preview": "package main\n\nimport \"fmt\"\n\ntype LogicProvider struct{}\n\nfunc (lp LogicProvider) Process(data string) string {\n\treturn d"
},
{
"path": "books/go/ch07/iota.go",
"chars": 111,
"preview": "package main\n\ntype MailCategory int\n\nconst (\n\tUncategorized MailCategory = iota\n\tPersonal\n\tSpam\n\tSocial\n\tAds\n)\n"
},
{
"path": "books/go/ch07/types.go",
"chars": 459,
"preview": "package main\n\nimport \"fmt\"\n\ntype Person struct {\n\tFirstName string\n\tLastName string\n\tAge int\n}\n\ntype King Person "
},
{
"path": "books/go/ch08/customErrors.go",
"chars": 271,
"preview": "package main\n\ntype Status int\n\nconst (\n\tInvalidLogin Status = iota + 1\n\tNotFound\n)\n\ntype StatusErr struct {\n\tStatus Sta"
},
{
"path": "books/go/ch08/errors.go",
"chars": 459,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc calcRemainderAndMod(numerator, denominator int) (int, int, error) "
},
{
"path": "books/go/ch08/panic.go",
"chars": 88,
"preview": "package main\n\nfunc doPanic(msg string) {\n\tpanic(msg)\n}\n\nfunc main() {\n\tdoPanic(\"ERR\")\n}\n"
},
{
"path": "books/go/ch08/recover.go",
"chars": 220,
"preview": "package main\n\nimport \"fmt\"\n\nfunc div60(i int) {\n\tdefer func() {\n\t\tif v := recover(); v != nil {\n\t\t\tfmt.Println(v)\n\t\t}\n\t}"
},
{
"path": "books/go/ch08/sentinel.go",
"chars": 415,
"preview": "package main\n\nimport (\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"fmt\"\n)\n\ntype Sentinel string\n\nfunc (s Sentinel) Error() string {\n\tretur"
},
{
"path": "books/go/ch08/wrappingErrors.go",
"chars": 487,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc fileChecker(name string) error {\n\tf, err := os.Open(name)\n\tif err "
},
{
"path": "books/go/ch09/formatter/formatter.go",
"chars": 106,
"preview": "package print\n\nimport \"fmt\"\n\nfunc Format(num int) string {\n\treturn fmt.Sprintf(\"The number is %d\", num)\n}\n"
},
{
"path": "books/go/ch09/main.go",
"chars": 147,
"preview": "package main\n\nimport (\n\t\"./formatter\"\n\t\"./math\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tnum := math.Double(2)\n\toutput := print.Format(n"
},
{
"path": "books/go/ch09/math/math.go",
"chars": 55,
"preview": "package math\n\nfunc Double(a int) int {\n\treturn a * 2\n}\n"
},
{
"path": "books/go/ch10/deadlock.go",
"chars": 213,
"preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\n\tgo func() {\n\t\tv := 1\n\t\tch1 <- "
},
{
"path": "books/go/ch10/deadlockSolution.go",
"chars": 251,
"preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\n\tgo func() {\n\t\tv := 1\n\t\tch1 <- "
},
{
"path": "books/go/ch10/goroutinesExample.go",
"chars": 211,
"preview": "package main\n\nfunc process(val int) int {\n\treturn val * 2\n}\n\nfunc runThingConcurrently(in <-chan int, out chan<- int) {\n"
},
{
"path": "books/go/notes.md",
"chars": 44896,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Learning Go: An Idiomatic Approach to Real-World Go Programming\n\nBook "
},
{
"path": "books/hands-on-ml.md",
"chars": 5518,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow: Co"
},
{
"path": "books/head-first-design-patterns/ch_01_strategy.py",
"chars": 1267,
"preview": "class FlyBehavior:\n def fly(self) -> None:\n raise NotImplementedError\n\n\nclass QuackBehavior:\n def quack(sel"
},
{
"path": "books/head-first-design-patterns/ch_02_observer.py",
"chars": 2464,
"preview": "class Observer:\n def update(self) -> None:\n raise NotImplementedError\n\n\nclass Subject:\n def register_observ"
},
{
"path": "books/head-first-design-patterns/ch_03_decorator.py",
"chars": 1099,
"preview": "class Beverage:\n @property\n def description(self) -> str:\n return self.__class__.__name__\n\n @property\n "
},
{
"path": "books/head-first-design-patterns/ch_04_factory.py",
"chars": 4479,
"preview": "class Ingredient:\n def __init__(self):\n print(self.__class__.__name__)\n\n\nclass ThinCrustDough(Ingredient):\n "
},
{
"path": "books/head-first-design-patterns/ch_05_singleton.py",
"chars": 1290,
"preview": "class ChocolateBoiler:\n _instance = None\n\n def __new__(cls):\n if not cls._instance:\n cls._instan"
},
{
"path": "books/head-first-design-patterns/ch_06_command.py",
"chars": 3600,
"preview": "from typing import List\n\n\nclass Device:\n @property\n def name(self) -> str:\n return self.__class__.__name__\n"
},
{
"path": "books/head-first-design-patterns/ch_07_adapter.py",
"chars": 785,
"preview": "class Duck:\n def quack(self) -> None:\n raise NotImplementedError\n\n def fly(self) -> None:\n raise Not"
},
{
"path": "books/head-first-design-patterns/ch_07_facade.py",
"chars": 803,
"preview": "from unittest.mock import Mock\n\n\nclass HomeTheaterFacade:\n def __init__(self, amplifier, tuner, projector, lights, sc"
},
{
"path": "books/head-first-design-patterns/ch_08_template_method.py",
"chars": 850,
"preview": "class CaffeineBeverage:\n def prepare_recipe(self) -> None:\n self._boil_water()\n self._brew()\n se"
},
{
"path": "books/head-first-design-patterns/ch_09_composite.py",
"chars": 1788,
"preview": "from __future__ import annotations\n\nfrom abc import ABC\nfrom dataclasses import dataclass\n\n\nclass MenuComponent:\n def"
},
{
"path": "books/head-first-design-patterns/ch_09_iterator.py",
"chars": 2802,
"preview": "from collections.abc import Iterator\nfrom dataclasses import dataclass\nfrom typing import (\n Dict,\n List,\n Unio"
},
{
"path": "books/head-first-design-patterns/ch_10_state.py",
"chars": 3032,
"preview": "from __future__ import annotations\n\nfrom random import random\n\n\nclass State:\n def __init__(self, gumball_machine: Gum"
},
{
"path": "books/head-first-design-patterns/ch_11_virtual_proxy.py",
"chars": 1202,
"preview": "class Icon:\n @property\n def width(self) -> int:\n raise NotImplementedError\n\n @property\n def height(se"
},
{
"path": "books/head-first-design-patterns/notes.md",
"chars": 69839,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Head First Design Patterns: Building Extensible and Maintainable Objec"
},
{
"path": "books/kubernetes-book.md",
"chars": 39562,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# The Kubernetes Book\n\nBook by Nigel Poulton, https://github.com/nigelpo"
},
{
"path": "books/kubernetes-in-action.md",
"chars": 109,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Kubernetes in Action, Second Edition \n\nBook by Marko Lukša\n"
},
{
"path": "books/nlp-book.md",
"chars": 53249,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Speech and Language Processing: An Introduction to Natural Language Pr"
},
{
"path": "books/peopleware.md",
"chars": 35253,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Peopleware: Productive Projects and Teams\n\nBook by Tom DeMarco and Tim"
},
{
"path": "books/pragmatic-programmer.md",
"chars": 42307,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# The Pragmatic Programmer: journey to mastery, 20th Anniversary Edition"
},
{
"path": "books/pytest/.coveragerc",
"chars": 25,
"preview": "[paths]\nsource =\n src/"
},
{
"path": "books/pytest/Dockerfile",
"chars": 184,
"preview": "FROM python:3.10.2\n\nWORKDIR /src\n\nENV PYTHONPATH \"${PYTHONPATH}:/src\"\n\nCOPY requirements.txt .\nCOPY setup.cfg .\n\nRUN pip"
},
{
"path": "books/pytest/docker-compose.yml",
"chars": 119,
"preview": "version: \"3.9\"\nservices:\n book:\n build:\n context: .\n dockerfile: Dockerfile\n volumes:\n - ./:/src\n"
},
{
"path": "books/pytest/notes.md",
"chars": 28378,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Python Testing with Pytest: Simple, Rapid, Effective, and Scalable\n\nBo"
},
{
"path": "books/pytest/requirements.txt",
"chars": 62,
"preview": "tinydb\npytest\nfaker\ntox\ncoverage\npytest-cov\ntinydb\ntyper\nrich\n"
},
{
"path": "books/pytest/setup.cfg",
"chars": 49,
"preview": "[tool:pytest]\npython_paths = .\ntestpaths = tests\n"
},
{
"path": "books/pytest/src/__init__.py",
"chars": 115,
"preview": "\"\"\"Top-level package for cards.\"\"\"\n\n__version__ = \"1.0.0\"\n\nfrom .api import * # noqa\nfrom .cli import app # noqa\n"
},
{
"path": "books/pytest/src/api.py",
"chars": 3169,
"preview": "\"\"\"\nAPI for the cards project\n\"\"\"\nfrom dataclasses import asdict\nfrom dataclasses import dataclass\nfrom dataclasses impo"
},
{
"path": "books/pytest/src/cli.py",
"chars": 3462,
"preview": "\"\"\"Command Line Interface (CLI) for cards project.\"\"\"\nimport os\nimport pathlib\nfrom contextlib import contextmanager\nfro"
},
{
"path": "books/pytest/src/db.py",
"chars": 856,
"preview": "\"\"\"\nDB for the cards project\n\"\"\"\nimport tinydb\n\n\nclass DB:\n def __init__(self, db_path, db_file_prefix):\n self"
},
{
"path": "books/pytest/tests/ch_02/test_card.py",
"chars": 1093,
"preview": "import pytest\n\nfrom src import Card\n\n\ndef test_field_access():\n c = Card(\"something\", \"brian\", \"todo\", 123)\n asser"
},
{
"path": "books/pytest/tests/ch_02/test_classes.py",
"chars": 458,
"preview": "from src import Card\n\n\nclass TestEquality:\n def test_equality(self):\n assert Card(\"something\", \"brian\", \"todo\""
},
{
"path": "books/pytest/tests/ch_02/test_exceptions.py",
"chars": 246,
"preview": "import pytest\nfrom src import CardsDB\n\n\ndef test_no_path_raises():\n with pytest.raises(TypeError):\n CardsDB()\n"
},
{
"path": "books/pytest/tests/ch_02/test_helper.py",
"chars": 467,
"preview": "import pytest\n\nfrom src import Card\n\n\ndef assert_identical(c1: Card, c2: Card):\n # Do not include 'assert_identical' "
},
{
"path": "books/pytest/tests/ch_03/conftest.py",
"chars": 664,
"preview": "from pathlib import Path\nfrom tempfile import TemporaryDirectory\n\nimport pytest\n\nfrom src import (\n Card,\n CardsDB"
},
{
"path": "books/pytest/tests/ch_03/test_autouse.py",
"chars": 660,
"preview": "from time import (\n localtime,\n sleep,\n strftime,\n time,\n)\n\nimport pytest\n\n\n@pytest.fixture(scope=\"function\""
},
{
"path": "books/pytest/tests/ch_03/test_count.py",
"chars": 389,
"preview": "from src import Card\n\n\ndef test_empty(cards_db):\n assert cards_db.count() == 0\n\n\ndef test_two(cards_db):\n cards_db"
},
{
"path": "books/pytest/tests/ch_03/test_count_initial.py",
"chars": 286,
"preview": "from pathlib import Path\nfrom tempfile import TemporaryDirectory\n\nfrom src import CardsDB\n\n\ndef test_empty():\n with T"
},
{
"path": "books/pytest/tests/ch_03/test_fixtures.py",
"chars": 125,
"preview": "import pytest\n\n\n@pytest.fixture()\ndef some_data():\n return 42\n\n\ndef test_some_data(some_data):\n assert some_data ="
},
{
"path": "books/pytest/tests/ch_03/test_rename_fixture.py",
"chars": 174,
"preview": "import pytest\n\n\n@pytest.fixture(name=\"ultimate_answer\")\ndef ultimate_answer_fixture():\n return 42\n\n\ndef test_everythi"
},
{
"path": "books/pytest/tests/ch_03/test_some.py",
"chars": 416,
"preview": "import pytest\n\n\n@pytest.fixture(scope=\"function\")\ndef non_empty_db(cards_db, some_cards):\n for c in some_cards:\n "
},
{
"path": "books/pytest/tests/ch_04/conftest.py",
"chars": 206,
"preview": "import pytest\nfrom src import CardsDB\n\n\n@pytest.fixture(scope=\"session\")\ndef db(tmp_path_factory):\n db_path = tmp_pat"
},
{
"path": "books/pytest/tests/ch_04/test_config.py",
"chars": 882,
"preview": "import src as cards\nfrom typer.testing import CliRunner\n\n\ndef run_cards(*params):\n runner = CliRunner()\n result = "
},
{
"path": "books/pytest/tests/ch_04/test_tmp.py",
"chars": 316,
"preview": "def test_tmp_path(tmp_path):\n file = tmp_path / \"file.txt\"\n file.write_text(\"Hello\")\n assert file.read_text() ="
},
{
"path": "books/pytest/tests/ch_04/test_version.py",
"chars": 370,
"preview": "from typer.testing import CliRunner\n\nimport src as cards\n\n\ndef test_version(capsys):\n cards.cli.version()\n output "
},
{
"path": "books/pytest/tests/ch_05/test_parametrize.py",
"chars": 1308,
"preview": "import pytest\n\nfrom src import (\n Card,\n CardsDB,\n)\n\n\n@pytest.fixture(scope=\"session\")\ndef db(tmp_path_factory):\n "
},
{
"path": "books/pytest/tests/ch_06/pytest.ini",
"chars": 217,
"preview": "[pytest]\nmarkers =\n smoke: subset of tests\n exception: check for expected exceptions\n custom: run only ch_06/cu"
},
{
"path": "books/pytest/tests/ch_06/test_builtin.py",
"chars": 921,
"preview": "from pathlib import Path\nfrom tempfile import TemporaryDirectory\n\nimport pytest\nfrom packaging.version import parse\n\nfro"
},
{
"path": "books/pytest/tests/ch_06/test_custom.py",
"chars": 681,
"preview": "import pytest\n\nfrom src import (\n Card,\n CardsDB,\n InvalidCardId,\n)\n\npytestmark = [pytest.mark.custom]\n\n@pytest"
},
{
"path": "books/pytest/tests/ch_06/text_combination.py",
"chars": 759,
"preview": "import pytest\nfrom src import (\n Card,\n CardsDB,\n)\n\n\n@pytest.fixture(scope=\"session\")\ndef db(tmp_path_factory):\n "
},
{
"path": "books/pytest/tests/ch_12/hello.py",
"chars": 77,
"preview": "def main():\n print(\"Hello world\")\n\n\nif __name__ == '__main__':\n main()\n"
},
{
"path": "books/pytest/tests/ch_12/test_hello.py",
"chars": 147,
"preview": "from tests.ch_12 import hello\n\n\ndef test_hello(capsys):\n hello.main()\n output = capsys.readouterr().out\n assert"
},
{
"path": "books/pytest/tests/ch_15/conftest.py",
"chars": 519,
"preview": "import pytest\n\n\ndef pytest_configure(config):\n config.addinivalue_line(\"markers\", \"slow: mark test as slow to run\")\n\n"
},
{
"path": "books/pytest/tests/ch_15/pytest.ini",
"chars": 50,
"preview": "[pytest]\nmarkers = slow: mark test as slow to run\n"
},
{
"path": "books/pytest/tests/ch_15/test_slow.py",
"chars": 88,
"preview": "import pytest\n\n\ndef test_normal():\n pass\n\n@pytest.mark.slow\ndef test_slow():\n pass"
},
{
"path": "books/python-architecture-patterns/Dockerfile",
"chars": 184,
"preview": "FROM python:3.10.2\n\nWORKDIR /src\n\nENV PYTHONPATH \"${PYTHONPATH}:/src\"\n\nCOPY requirements.txt .\nCOPY setup.cfg .\n\nRUN pip"
},
{
"path": "books/python-architecture-patterns/Makefile",
"chars": 151,
"preview": "test-flake8:\n\tdocker-compose run --rm api flake8 .\n\ntest-mypy:\n\tdocker-compose run --rm api mypy .\n\ntest-pytest:\n\tdocker"
},
{
"path": "books/python-architecture-patterns/docker-compose.yml",
"chars": 1204,
"preview": "version: \"3.9\"\nservices:\n\n redis_pubsub:\n build:\n context: .\n dockerfile: Dockerfile\n image: allocation"
},
{
"path": "books/python-architecture-patterns/notes.md",
"chars": 31542,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Architecture Patterns with Python: Enabling Test-Driven Development, D"
},
{
"path": "books/python-architecture-patterns/requirements.txt",
"chars": 189,
"preview": "pytest==6.2.5\nmypy==0.931\nflake8==4.0.1\nSQLAlchemy==1.4.31\nfastapi==0.73.0\nsqlmodel==0.0.6\nrequests==2.27.1\npsycopg2==2."
},
{
"path": "books/python-architecture-patterns/setup.cfg",
"chars": 396,
"preview": "[tool:pytest]\npython_paths = .\ntestpaths = tests\nnorecursedirs = .*\naddopts = -sl\nfilterwarnings =\n ignore::Deprecati"
},
{
"path": "books/python-architecture-patterns/src/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "books/python-architecture-patterns/src/adapters/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "books/python-architecture-patterns/src/adapters/notifications.py",
"chars": 775,
"preview": "from abc import (\n ABC,\n abstractmethod,\n)\nimport smtplib\n\nfrom src import config\n\nDEFAULT_HOST = config.get_email"
},
{
"path": "books/python-architecture-patterns/src/adapters/orm.py",
"chars": 336,
"preview": "from sqlmodel import (\n Field,\n SQLModel,\n)\n\n\nclass AllocationsView(SQLModel, table=True):\n id: int = Field(pri"
},
{
"path": "books/python-architecture-patterns/src/adapters/redis_publisher.py",
"chars": 218,
"preview": "from redis.client import Redis\n\nfrom src import config\nfrom src.domain.events import Event\n\nr = Redis(**config.get_redis"
},
{
"path": "books/python-architecture-patterns/src/adapters/repository.py",
"chars": 1556,
"preview": "from typing import (\n Optional,\n Protocol,\n Set,\n)\n\nfrom sqlmodel import (\n Session,\n select,\n)\n\nfrom src"
},
{
"path": "books/python-architecture-patterns/src/app.py",
"chars": 1168,
"preview": "from fastapi import (\n FastAPI,\n Response,\n status,\n)\n\nfrom src import views\nfrom src.bootstrap import bootstra"
},
{
"path": "books/python-architecture-patterns/src/bootstrap.py",
"chars": 1707,
"preview": "import inspect\nfrom typing import Callable\n\nfrom sqlalchemy.engine import Engine\nfrom sqlmodel import create_engine\n\nfro"
},
{
"path": "books/python-architecture-patterns/src/config.py",
"chars": 852,
"preview": "import os\n\n\ndef get_postgres_uri():\n host = os.environ.get(\"DB_HOST\", \"localhost\")\n port = 54321 if host == \"local"
},
{
"path": "books/python-architecture-patterns/src/domain/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "books/python-architecture-patterns/src/domain/commands.py",
"chars": 382,
"preview": "from dataclasses import dataclass\nfrom datetime import date\nfrom typing import Optional\n\n\nclass Command:\n pass\n\n\n@dat"
},
{
"path": "books/python-architecture-patterns/src/domain/events.py",
"chars": 336,
"preview": "from pydantic import BaseModel\n\n\nclass Event(BaseModel):\n pass\n\n\nclass OutOfStock(Event):\n sku: str\n\n\nclass Alloca"
},
{
"path": "books/python-architecture-patterns/src/domain/model.py",
"chars": 3814,
"preview": "from datetime import date\nfrom typing import (\n Iterable,\n List,\n Optional,\n Union,\n cast,\n)\n\nfrom pydant"
},
{
"path": "books/python-architecture-patterns/src/redis_consumer.py",
"chars": 778,
"preview": "import json\nfrom typing import Dict\n\nfrom redis.client import Redis\n\nfrom src import config\nfrom src.bootstrap import bo"
},
{
"path": "books/python-architecture-patterns/src/service_layer/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "books/python-architecture-patterns/src/service_layer/handlers.py",
"chars": 2692,
"preview": "from src.adapters import redis_publisher\nfrom src.adapters.notifications import AbstractNotifications\nfrom src.domain im"
},
{
"path": "books/python-architecture-patterns/src/service_layer/message_bus.py",
"chars": 2413,
"preview": "import logging\nfrom typing import (\n Callable,\n Dict,\n List,\n Type,\n Union,\n)\n\nfrom src.domain import (\n "
},
{
"path": "books/python-architecture-patterns/src/service_layer/unit_of_work.py",
"chars": 1564,
"preview": "from __future__ import annotations\n\nfrom abc import (\n ABC,\n abstractmethod,\n)\nfrom typing import Optional\n\nfrom s"
},
{
"path": "books/python-architecture-patterns/src/views.py",
"chars": 386,
"preview": "from typing import (\n Dict,\n List,\n)\n\nfrom src.service_layer.unit_of_work import UnitOfWork\n\n\ndef allocations(orde"
},
{
"path": "books/python-architecture-patterns/tests/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "books/python-architecture-patterns/tests/conftest.py",
"chars": 1336,
"preview": "import pytest\nimport redis\nfrom sqlmodel import (\n Session,\n create_engine,\n)\nfrom starlette.testclient import Tes"
},
{
"path": "books/python-architecture-patterns/tests/e2e/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "books/python-architecture-patterns/tests/e2e/api_client.py",
"chars": 498,
"preview": "import json\n\nfrom src.domain.model import (\n Batch,\n OrderLine,\n)\n\n\ndef post_to_allocate(client, order_id, sku, qt"
},
{
"path": "books/python-architecture-patterns/tests/e2e/redis_client.py",
"chars": 384,
"preview": "import json\nimport redis\n\nfrom src import config\n\nr = redis.Redis(**config.get_redis_host_and_port())\n\n\ndef subscribe_to"
},
{
"path": "books/python-architecture-patterns/tests/e2e/test_app.py",
"chars": 1670,
"preview": "from datetime import date\nfrom uuid import uuid4\n\nfrom tests.e2e.api_client import (\n get_allocation,\n post_to_add"
},
{
"path": "books/python-architecture-patterns/tests/e2e/test_external_events.py",
"chars": 1457,
"preview": "import json\nfrom datetime import date\n\nimport pytest\nfrom tenacity import (\n Retrying,\n stop_after_delay,\n)\n\nfrom "
},
{
"path": "books/python-architecture-patterns/tests/integration/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "books/python-architecture-patterns/tests/integration/test_uow.py",
"chars": 2841,
"preview": "from threading import Thread\nfrom time import sleep\nfrom typing import List\n\nimport pytest\nfrom sqlalchemy.orm import se"
},
{
"path": "books/python-architecture-patterns/tests/integration/test_views.py",
"chars": 1732,
"preview": "from datetime import date\nfrom unittest.mock import Mock\n\nimport pytest\nfrom sqlmodel import Session\n\nfrom src import vi"
},
{
"path": "books/python-architecture-patterns/tests/unit/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "books/python-architecture-patterns/tests/unit/test_batches.py",
"chars": 1713,
"preview": "from datetime import date\n\nfrom src.domain.model import (\n Batch,\n OrderLine,\n)\n\n\ndef batch_and_line(sku, batch_qu"
},
{
"path": "books/python-architecture-patterns/tests/unit/test_handlers.py",
"chars": 4806,
"preview": "from __future__ import annotations\n\nfrom collections import defaultdict\nfrom datetime import date\nfrom typing import (\n "
},
{
"path": "books/python-architecture-patterns/tests/unit/test_product.py",
"chars": 2395,
"preview": "from datetime import date\n\nfrom src.domain import events\nfrom src.domain.model import (\n Batch,\n OrderLine,\n Pr"
},
{
"path": "books/refactoring.md",
"chars": 34841,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Refactoring: Improving the Design of Existing Code\n\nBook by Martin Fow"
},
{
"path": "books/release-it.md",
"chars": 67828,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Release It! Design and Deploy Production-Ready Software\n\nBook by Micha"
},
{
"path": "books/system-design-interview.md",
"chars": 175,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# System Design Interview\n\nBook by Alex Xu & Sahn Lam\n\n- [1. Proximity S"
},
{
"path": "books/tidy-first.md",
"chars": 8903,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Tidy First?\n\nBook by Kent Beck\n\n- [1. Guard Classes](#1-guard-classes)"
},
{
"path": "books/understanding-distributed-systems.md",
"chars": 181,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Understanding Distributed Systems: What every developer should know ab"
},
{
"path": "case-studies/reddit.md",
"chars": 3210,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# How Reddit mastered managing growth\n\n*Presentation by Greg Taylor*\n\n33"
},
{
"path": "conferences/aws-innovate-ai-ml-21.md",
"chars": 4327,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# AWS Innovate: AI/ML Edition 2021\n\n- [Move and scale your ML experiment"
},
{
"path": "conferences/brown-bags.md",
"chars": 2669,
"preview": "[go back](https://github.com/pkardas/learning)\n\n- [NLP - State of the Art](#nlp---state-of-the-art)\n- [Kanban Training]("
},
{
"path": "conferences/pycon-2022.md",
"chars": 5275,
"preview": "[go back](https://github.com/pkardas/learning)\n\n- [[EN] Don’t use a lot where a little will do. A story of programming t"
},
{
"path": "courses/fast-ai.md",
"chars": 7219,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Practical Deep Learning for Coders\n\nCourse -> https://course.fast.ai/\n"
},
{
"path": "patterns/abbreviations.md",
"chars": 6975,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Abbreviations\n\n- [SOLID](#solid)\n- [DRY - Don't Repeat Yourself](#dry-"
},
{
"path": "patterns/architecture.md",
"chars": 5063,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Architecture Patterns\n\n- [Command and Query Responsibility Segregation"
},
{
"path": "teaching/python-intermediate/README.md",
"chars": 142,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Python Intermediate\n\nRepository with the code and tasks: https://githu"
},
{
"path": "teaching/python-intro/README.md",
"chars": 524,
"preview": "[go back](https://github.com/pkardas/learning)\n\n# Introduction to Programming: Python for beginners\n\nThis folder contain"
},
{
"path": "teaching/python-intro/notebook.ipynb",
"chars": 17101,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"Introduction to programming: Pyt"
}
]
About this extraction
This page contains the full source code of the pkardas/notes GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 180 files (904.1 KB), approximately 207.1k tokens, and a symbol index with 693 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.