Clean Code Principles And Patterns
Clean Code Principles And Patterns
Python Edition
About the Book
There is also a Java/JavaScript/TypeScript (a couple of examples also in C++) edition of this book available: https://leanpub.com/cleancodeprinciplesandpatterns
Paperback version of the book is available on Amazon.
Clean Code Principles and Patterns: Python Edition is one of the most comprehensive no-fluff guides for Python software developers to help them write clean code every day. The author, Petri Silén, has almost 30 years of industry experience in designing and implementing software, and now he puts all his knowledge gained during the years into this book. The book is packed with principles and patterns that help developers, from novices and juniors to seniors and experts, to write cleaner code. The principles and patterns presented in the book are accompanied by realistic yet straightforward examples to help the reader understand them better. The reader should have basic knowledge of Python programming language to get the full benefit from this book. Also, having basic knowledge of Docker and Kubernetes concepts is beneficial. The source code for all major examples presented in the book is available in a public GitHub repository.
The book is divided into ten chapters:
- Architectural design principles
- Object-oriented design principles
- Coding principles
- Testing principles
- Security principles
- API design principles
- Database types and related principles
- Concurrent programming principles
- Agile and teamwork principles
- DevSecOps
After reading this book, you will know the following and much more:
- How to architect modern cloud-native microservices (examples with Kubernetes)
- What are autopilot microservices
- What are event sourcing, CQRS, distributed transactions, saga orchestration pattern, and saga choreography pattern
- What are the main concepts of object-oriented programming
- What are the different programming paradigms (imperative, functional, OO)
- What are the five SOLID principles, and how to put them into use in real-life code
- What are the 25 design patterns, and how to use them
- Law of Demeter, Tell, don't ask principle, YAGNI, primitive obsession
- What is the MVC pattern, and how MVP and MVVM differ from each other
- How to achieve a clean design for a microservice (hexagonal/clean architecture). What is a vertical slice architecture
- Why and how to use dependency injection
- Detailed instructions with concrete examples on how to uniformly name various software entities like classes, functions, and variables
- Why you should prefer composition over inheritance
- Strategical and tactical domain driven design (DDD)
- How to organize a source code repository
- How to organize code into directories
- Concrete ways how to avoid writing comments and refactor comments away
- What are the most common issues that static code analyzers find, and how to correct them
- Typical code smells and refactoring techniques to solve them
- Why you should use type annotations
- How to correctly handle errors and exceptions
- How not to forget to handle errors and exceptions
- How to avoid off-by-one errors effectively
- What you should remember when using a Google search or Generative AI like ChatGPT to get answers
- When and how to optimize code
- TDD, London vs. Detroit/Chicago schools, BDD, ATDD, unit testing, mocking, integration testing, E2E testing, and non-functional testing
- What is threat modeling and how to conduct it
- Authentication and authorization using OpenID Connect and OAuth2
- What are the essential security features to implement in an application
- How to design APIs using technologies like JSON-RPC, REST, GraphQL, SSE, WebSocket, gRPC, and event-driven services
- When and how to use a relational database, document database (MongoDB), key-value store (Redis), or wide-column database (Cassandra)
- How to avoid SQL injection attacks using ORM or parameterized SQL queries
- When to use threading or parallel algorithms and how to ensure thread safety
- What principles to follow when working in a software development team
- What are DevOps, SecOps, containerization, and continuous integration (CI). What is the difference between continuous delivery (CD) and continuous deployment (CD). What is observability. Some examples of Docker, Kubernetes and GitHub Actions.
- And much more
Table of Contents
- 1:About the Author
- 2:Introduction
- 3:Architectural Principles and Patterns
- 3.1:Software Hierarchy
- 3.2:The Twelve-Factor App
- 3.3:Single Responsibility Principle
- 3.4:Uniform Naming Principle
- 3.5:Encapsulation Principle
- 3.6:Service Aggregation Principle
- 3.7:High Cohesion, Low Coupling Principle
- 3.8:Library Composition Principle
- 3.9:Avoid Duplication Principle
- 3.10:Externalized Service Configuration Principle
- 3.10.1:Environment Variables
- 3.10.2:Kubernetes ConfigMaps
- 3.10.3:Kubernetes Secrets
- 3.10.4:External Store
- 3.11:Service Substitution Principle
- 3.12:Inter-Service Communication Methods
- 3.12.1:Synchronous Communication Method
- 3.12.2:Asynchronous Communication Method
- 3.12.3:Shared Data Communication Method
- 3.13:Strategical Domain-Driven Design Principle
- 3.13.1:Strategical DDD Example 1: Mobile Telecom Network Analytics Software System
- 3.13.2:Strategical DDD Example 2: Banking Software System
- 3.14:Autopilot Microservices Principle
- 3.14.1:Stateless Microservices Principle
- 3.14.2:Resilient Microservices Principle
- 3.14.3:Horizontally Autoscaling Microservices Principle
- 3.14.4:Highly-Available Microservices Principle
- 3.14.5:Observable Microservices Principle
- 3.15:Individually Deployable Microservices Principle
- 3.16:Software Versioning Principles
- 3.16.1:Use Semantic Versioning Principle
- 3.16.2:Avoid Using 0.x Versions Principle
- 3.16.3:Don’t Increase Major Version Principle
- 3.16.4:Implement Security Patches and Bug Corrections to All Major Versions Principle
- 3.16.5:Avoid Using Non-LTS Versions in Production Principle
- 3.17:Git Version Control Principle
- 3.17.1:Feature Branch
- 3.17.2:Feature Toggle
- 3.18:Architectural Patterns
- 3.18.1:Multi-Container Design Patterns
- 3.18.1.1:Sidecar Pattern
- 3.18.1.2:Ambassador Pattern
- 3.18.1.3:Adapter Pattern
- 3.18.2:Circuit Breaker Pattern
- 3.18.3:Competing Consumers Pattern
- 3.18.4:API Gateway Offloading Pattern
- 3.18.5:Retry Pattern
- 3.18.6:Static Content Hosting Pattern
- 3.18.7:Event Sourcing Pattern
- 3.18.8:Command Query Responsibility Segregation (CQRS) Pattern
- 3.18.9:Distributed Transaction Patterns
- 3.18.9.1:Saga Orchestration Pattern
- 3.18.9.2:Saga Choreography Pattern
- 3.18.1:Multi-Container Design Patterns
- 3.19:Preferred Technology Stacks Principle
- 3.20:8 Fallacies of distributed computing
- 4:Object-Oriented Design Principles
- 4.1:Object-Oriented Programming Concepts
- 4.1.1:Classes/Objects
- 4.1.2:Encapsulation
- 4.1.3:Abstraction
- 4.1.4:Inheritance
- 4.1.5:Interface
- 4.1.5.1:Interface evolution
- 4.1.6:Polymorphism
- 4.2:Programming Paradigms
- 4.2.1:Imperative Programming
- 4.2.2:Functional Programming
- 4.3:Multi-Paradigm Programming Principle
- 4.4:Why is Object-Oriented Programming Hard?
- 4.5:SOLID Principles
- 4.5.1:Single Responsibility Principle
- 4.5.2:Open-Closed Principle
- 4.5.3:Liskov’s Substitution Principle
- 4.5.4:Interface Segregation and Multiple Inheritance Principle
- 4.5.5:Program Against Interfaces Principle (Generalized Dependency Inversion Principle)
- 4.6:Clean Architecture Principle
- 4.6.1:Real-Life Example
- 4.7:Vertical Slice Architecture Principle
- 4.8:Class Organization Principle
- 4.9:Package, Class and Function Sizing Principle
- 4.10:Uniform Naming Principle
- 4.10.1:Naming Interfaces and Classes
- 4.10.2:Naming Functions
- 4.10.2.1:Preposition in Function Name
- 4.10.2.2:Naming Method Pairs
- 4.10.2.3:Naming Boolean Functions (Predicates)
- 4.10.2.4:Naming Builder Methods
- 4.10.2.5:Naming Methods with Implicit Verbs
- 4.10.2.6:Naming Lifecycle Methods
- 4.10.2.7:Naming Generic Type Parameters
- 4.10.2.8:Naming Function Parameters
- 4.11:Encapsulation Principle
- 4.11.1:Immutable Objects
- 4.11.2:Don’t Leak Modifiable Internal State Outside an Object Principle
- 4.11.3:Don’t Assign From a Method Parameter to a Modifiable Attribute
- 4.12:Prefer Composition Over Inheritance Principle
- 4.13:Tactical Domain-Driven Design Principle
- 4.13.1:Tactical DDD Concepts
- 4.13.1.1:Entities
- 4.13.1.2:Value Objects
- 4.13.1.3:Aggregates
- 4.13.1.4:Aggregate Roots
- 4.13.1.5:Actors
- 4.13.1.6:Factories
- 4.13.1.7:Repositories
- 4.13.1.8:Services
- 4.13.1.9:Events
- 4.13.1.10:Design-Level Event Storming
- 4.13.2:Tactical DDD Example 1: Data Exporter Microservice
- 4.13.3:Tactical DDD Example 2: Anomaly Detection Microservice
- 4.13.1:Tactical DDD Concepts
- 4.14:Design Patterns
- 4.14.1:Design Patterns for Creating Objects
- 4.14.1.1:Factory Pattern
- 4.14.1.2:Abstract Factory Pattern
- 4.14.1.3:Static Factory Method Pattern
- 4.14.1.4:Builder Pattern
- 4.14.1.5:Singleton Pattern
- 4.14.1.6:Prototype Pattern
- 4.14.1.7:Object Pool Pattern
- 4.14.2:Structural Design Patterns
- 4.14.2.1:Composite Pattern
- 4.14.2.2:Facade Pattern
- 4.14.2.3:Bridge Pattern
- 4.14.2.4:Strategy Pattern
- 4.14.2.5:Adapter Pattern
- 4.14.2.6:Proxy Pattern
- 4.14.2.7:Decorator Pattern
- 4.14.2.8:Flyweight Pattern
- 4.14.3:Behavioral Design Patterns
- 4.14.3.1:Chain of Responsibility Pattern
- 4.14.3.2:Observer Pattern
- 4.14.3.3:Command/Action Pattern
- 4.14.3.4:Iterator Pattern
- 4.14.3.5:Interpreter Pattern
- 4.14.3.6:State Pattern
- 4.14.3.7:Mediator Pattern
- 4.14.3.8:Template Method Pattern
- 4.14.3.9:Memento Pattern
- 4.14.3.10:Visitor Pattern
- 4.14.3.11:Null Object Pattern
- 4.14.1:Design Patterns for Creating Objects
- 4.15:Tell, Don’t Ask Principle
- 4.16:Law of Demeter
- 4.17:Avoid Primitive Obsession Principle
- 4.18:You Aren’t Gonna Need It (YAGNI) Principle
- 4.19:Dependency Injection (DI) Principle
- 4.20:Avoid Code Duplication Principle
- 4.21:Inheritance in Cascading Style Sheets (CSS)
- 4.1:Object-Oriented Programming Concepts
- 5:Coding Principles
- 5.1:Uniform Variable Naming Principle
- 5.1.1:Naming Integer Variables
- 5.1.2:Naming Floating-Point Number Variables
- 5.1.3:Naming Boolean Variables
- 5.1.4:Naming String Variables
- 5.1.5:Naming Enum Variables
- 5.1.6:Naming Collection (List and Set) Variables
- 5.1.7:Naming Dictionary Variables
- 5.1.8:Naming Pair and Tuple Variables
- 5.1.9:Naming Object Variables
- 5.1.10:Naming Optional Variables
- 5.1.11:Naming Function Variables (Callbacks)
- 5.1.12:Naming Class Attributes
- 5.1.13:General Naming Rules
- 5.1.13.1:Use Short, Common Names
- 5.1.13.2:Pick One Term And Use It Consistently
- 5.1.13.3:Avoid Obscure Abbreviations
- 5.1.13.4:Avoid Too Short Or Meaningless Names
- 5.2:Uniform Source Code Repository Structure Principle
- 5.3:Domain-Based Source Code Structure Principle
- 5.4:Avoid Comments Principle
- 5.4.1:Name Things Properly
- 5.4.2:Single Return Of Named Value At The End Of Function
- 5.4.3:Return Type Aliasing
- 5.4.4:Extract Constant for Boolean Expression
- 5.4.5:Extract Named Constant or Enumerated Type
- 5.4.6:Extract Function
- 5.4.7:Avoid Comments for Regular Expression
- 5.4.8:Avoiding Comments in Bash Shell Scripts
- 5.5:Function Single Return Principle
- 5.6:Use Type Annotations for Production Code Principle
- 5.6.1:Function Arguments Might Be Given in Wrong Order
- 5.6.2:Function Argument Might Be Given with Wrong Type
- 5.6.3:Function Return Value Type Might Be Misunderstood
- 5.6.4:Refactoring Code Is More Difficult
- 5.6.5:Forced to Write Public API Comments
- 5.6.6:Type Errors Are Not Found in Testing
- 5.7:Refactoring Principle
- 5.7.1:Rename
- 5.7.2:Extract Method
- 5.7.3:Extract Class
- 5.7.4:Extract Constant
- 5.7.5:Replace Conditionals with Polymorphism
- 5.7.6:Introduce Parameter Object
- 5.7.7:Invert If-Statement
- 5.7.8:Creating Rich Object
- 5.8:Static Code Analysis Principle
- 5.8.1:Common Static Code Analysis Issues
- 5.9:Error/Exception Handling Principle
- 5.9.1:Returning Errors
- 5.9.1.1:Returning Failure Indicator
- 5.9.1.2:Returning an Optional Value
- 5.9.1.3:Returning an Error Object
- 5.9.1.4:Adapt to Wanted Error Handling Mechanism
- 5.9.1.5:Functional Exception Handling
- 5.9.1:Returning Errors
- 5.10:Avoid Off-By-One Errors Principle
- 5.11:Be Critical When Googling or Using Generative AI Principle
- 5.12:Make One Change At A Time Principle
- 5.13:Choosing Right 3rd Party Component Principle
- 5.14:Use Appropriate Data Structure Principle
- 5.14.1:List
- 5.14.2:Dictionary
- 5.14.3:Tuple
- 5.14.4:Set
- 5.14.5:String
- 5.14.6:Bytearray
- 5.14.7:Counter
- 5.14.8:OrderedDict
- 5.14.9:Deque (Double Ended Queue)
- 5.14.10:Customized Built-In Collections
- 5.14.11:Stack (LIFO Queue)
- 5.14.12:Queue (FIFO Queue)
- 5.14.13:Priority Queue
- 5.14.14:Synchronized Queues (LIFO/FIFO)
- 5.15:Optimization Principle
- 5.15.1:Optimization Patterns
- 5.15.1.1:Optimize Busy Loops Only Pattern
- 5.15.1.2:Remove Unnecessary Functionality Pattern
- 5.15.1.3:Object Pool Pattern
- 5.15.1.4:Use Optimal Data Structures Pattern
- 5.15.1.5:Algorithm Complexity Reduction Pattern
- 5.15.1.6:Cache Function Results Pattern
- 5.15.1.7:Buffer File I/O Pattern
- 5.15.1.8:Share Identical Objects a.k.a Flyweight Pattern
- 5.15.1:Optimization Patterns
- 5.1:Uniform Variable Naming Principle
- 6:Testing Principles
- 6.1:Functional Testing Principles
- 6.1.1:Unit Testing Principle
- 6.1.1.1:TDD Schools: London vs. Detroit/Chicago
- 6.1.1.2:Test-Driven Development (TDD)
- 6.1.1.3:Unit Specification-Driven Development (USDD)
- 6.1.1.4:Naming Conventions
- 6.1.1.5:Mocking
- 6.1.1.6:Web UI Component Testing
- 6.1.2:Software Component Integration Testing Principle
- 6.1.2.1:Web UI Integration Testing
- 6.1.2.2:Setting Up Integration Testing Environment
- 6.1.3:Complete Example with BDD, ATDD, DDD, OOD and TDD
- 6.1.4:End-to-End (E2E) Testing Principle
- 6.1.1:Unit Testing Principle
- 6.2:Non-Functional Testing Principle
- 6.2.1:Performance Testing
- 6.2.2:Data Volume Testing
- 6.2.3:Stability Testing
- 6.2.4:Reliability Testing
- 6.2.5:Stress and Scalability Testing
- 6.2.6:Security Testing
- 6.2.7:Other Non-Functional Testing
- 6.2.7.1:Visual Testing
- 6.1:Functional Testing Principles
- 7:Security Principles
- 7.1:Shift Security to Left Principle
- 7.2:Have a Product Security Lead Principle
- 7.3:Use Threat Modelling Process Principle
- 7.3.1:Decompose Application
- 7.3.2:Determine and Rank Threats
- 7.3.2.1:STRIDE method
- 7.3.2.2:STRIDE Threat Examples
- 7.3.2.3:Application Security Frame (ASF) method
- 7.3.3:Determine Countermeasures and Mitigation
- 7.3.4:Threat Modeling Example using STRIDE
- 7.3.4.1:Decompose Application
- 7.3.4.2:Determine and Rank Threats
- 7.3.5:Determine Countermeasures and Mitigation
- 7.3.6:Threat Modeling Example Using ASF
- 7.4:Security Features
- 7.4.1:Authentication and Authorization
- 7.4.1.1:OpenID Connect Authentication and Authorization in Frontend
- 7.4.1.2:OAuth2 Authorization in Backend
- 7.4.2:Password Policy
- 7.4.3:Cryptography
- 7.4.3.1:Encryption Key Lifetime and Rotation
- 7.4.4:Denial-of-service (DoS) Prevention
- 7.4.5:Database Security
- 7.4.6:SQL Injection Prevention
- 7.4.7:OS Command Injection Prevention
- 7.4.8:Security Configuration
- 7.4.9:Automatic Vulnerability Scanning
- 7.4.10:Integrity
- 7.4.11:Error Handling
- 7.4.12:Logging
- 7.4.13:Audit Logging
- 7.4.14:Input Validation
- 7.4.14.1:Validating Numbers
- 7.4.14.2:Validating Strings
- 7.4.14.3:Validating Timestamps
- 7.4.14.4:Validating Arrays
- 7.4.14.5:Validating Objects
- 7.4.14.6:Validating Files Uploaded to Server
- 7.4.1:Authentication and Authorization
- 8:API Design Principles
- 8.1:Frontend Facing API Design Principles
- 8.1.1:JSON-based RPC API Design Principle
- 8.1.2:REST API Design Principle
- 8.1.2.1:Creating a Resource
- 8.1.2.2:Reading Resources
- 8.1.2.3:Updating Resources
- 8.1.2.4:Deleting Resources
- 8.1.2.5:Executing Non-CRUD Actions on Resources
- 8.1.2.6:Resource Composition
- 8.1.2.7:HTTP Status Codes
- 8.1.2.8:HATEOAS and HAL
- 8.1.2.9:API Versioning
- 8.1.2.10:Documentation
- 8.1.2.11:Implementation Example
- 8.1.3:GraphQL API Design
- 8.1.4:Subscription-Based API Design
- 8.1.4.1:Server-Sent Events (SSE)
- 8.1.4.2:GraphQL Subscriptions
- 8.1.5:WebSocket Example
- 8.2:Inter-Microservice API Design Principles
- 8.2.1:Synchronous API Design Principle
- 8.2.1.1:gRPC-Based API Design Example
- 8.2.2:Asynchronous API Design Principle
- 8.2.2.1:Request-Only Asynchronous API Design
- 8.2.2.2:Request-Response Asynchronous API Design
- 8.2.2.3:Asynchronous API Documentation
- 8.2.1:Synchronous API Design Principle
- 8.3:API Design Example
- 8.1:Frontend Facing API Design Principles
- 9:Databases And Database Principles
- 9.1:Relational Databases
- 9.1.1:Structure of Relational Database
- 9.1.2:Use Object Relational Mapper (ORM) Principle
- 9.1.2.1:Entity/Table Relationships
- 9.1.2.1.1:One-To-One/Many Relationships
- 9.1.2.1.2:Many-To-Many Relationships
- 9.1.2.2:Sales Item Repository Example
- 9.1.2.1:Entity/Table Relationships
- 9.1.3:Use Parameterized SQL Statements Principle
- 9.1.3.1:Sales Item Repository Example
- 9.1.4:Normalization Rules
- 9.1.4.1:First Normal Form (1NF)
- 9.1.4.2:Second Normal Form (2NF)
- 9.1.4.3:Third Normal Form (3NF)
- 9.2:Document Databases
- 9.2.1:Sales Item Repository Example
- 9.3:Key-Value Database Principle
- 9.4:Wide-Column Databases
- 9.5:Search Engines
- 9.1:Relational Databases
- 10:Concurrent Programming Principles
- 10.1:Threading Principle
- 10.1.1:Parallel Executors
- 10.2:Thread Safety Principle
- 10.2.1:Use Locking for Mutual Exclusion (Mutex)
- 10.2.2:Atomic Variables
- 10.2.3:Concurrent Collections
- 10.3:Publish/Subscribe Shared State Change Principle
- 10.1:Threading Principle
- 11:Agile and Teamwork Principles
- 11.1:Twelve Principles of Agile Software
- 11.2:Use Agile Framework Principle
- 11.3:Define the Done Principle
- 11.4:You Write Code for Other People Principle
- 11.5:Avoid Technical Debt Principle
- 11.6:Software Component Documentation Principle
- 11.7:Code Review Principle
- 11.7.1:Focus on Object-Oriented Design
- 11.7.2:Focus on Removal of Duplicate Information (DRY principle)
- 11.7.3:Focus on Spreading Knowledge
- 11.7.4:Focus on Function Specification by Unit Tests
- 11.7.5:Focus on Proper and Uniform Naming
- 11.7.6:Don’t Focus on Premature Optimization
- 11.7.7:Detect Possible Malicious Code
- 11.8:Uniform Code Formatting Principle
- 11.9:Highly Concurrent Development Principle
- 11.9.1:Dedicated Microservices and Microlibraries
- 11.9.2:Dedicated Domains
- 11.9.3:Follow Open-Closed Principle
- 11.10:Pair Programming Principle
- 11.11:Mob Programming Principle
- 11.12:Ask and Offer Help Principle
- 11.13:Well-Defined Development Team Roles Principle
- 11.13.1:Product Owner
- 11.13.2:Scrum Master
- 11.13.3:Software Developer
- 11.13.4:Test Automation Developer
- 11.13.5:DevOps Engineer
- 11.13.6:UI Designer
- 11.14:Competence Transfer Principle
- 11.15:Inter-Team Communication Principle
- 12:DevSecOps
- 12.1:SecOps Lifecycle
- 12.2:DevOps Lifecycle
- 12.2.1:Plan
- 12.2.2:Code
- 12.2.3:Build and Test
- 12.2.4:Release
- 12.2.4.1:Example Dockerfile
- 12.2.4.2:Example Kubernetes Deployment
- 12.2.4.3:Example CI/CD Pipeline
- 12.2.5:Deploy
- 12.2.6:Operate
- 12.2.7:Monitor
- 12.2.7.1:Logging to Standard Input
- 12.2.7.2:Distributed Tracing
- 12.2.7.3:Metrics Collection
- 12.2.7.4:Metrics Visualization
- 12.2.7.5:Alerting
- 12.2.8:Software System Alerts Dashboard Example
- 12.2.9:Microservice Grafana Dashboard Example
- 12.2.9.1:Logging
- 12.2.9.2:OpenTelemetry Log Data Model
- 12.2.9.3:PrometheusRule Example
- 13:Conclusion
Other books by this author
The Leanpub 60 Day 100% Happiness Guarantee
Within 60 days of purchase you can get a 100% refund on any Leanpub purchase, in two clicks.
Now, this is technically risky for us, since you'll have the book or course files either way. But we're so confident in our products and services, and in our authors and readers, that we're happy to offer a full money back guarantee for everything we sell.
You can only find out how good something is by trying it, and because of our 100% money back guarantee there's literally no risk to do so!
So, there's no reason not to click the Add to Cart button, is there?
See full terms...
Earn $8 on a $10 Purchase, and $16 on a $20 Purchase
We pay 80% royalties on purchases of $7.99 or more, and 80% royalties minus a 50 cent flat fee on purchases between $0.99 and $7.98. You earn $8 on a $10 sale, and $16 on a $20 sale. So, if we sell 5000 non-refunded copies of your book for $20, you'll earn $80,000.
(Yes, some authors have already earned much more than that on Leanpub.)
In fact, authors have earnedover $13 millionwriting, publishing and selling on Leanpub.
Learn more about writing on Leanpub
Free Updates. DRM Free.
If you buy a Leanpub book, you get free updates for as long as the author updates the book! Many authors use Leanpub to publish their books in-progress, while they are writing them. All readers get free updates, regardless of when they bought the book or how much they paid (including free).
Most Leanpub books are available in PDF (for computers) and EPUB (for phones, tablets and Kindle). The formats that a book includes are shown at the top right corner of this page.
Finally, Leanpub books don't have any DRM copy-protection nonsense, so you can easily read them on any supported device.
Learn more about Leanpub's ebook formats and where to read them