├── cra-monaco ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── src │ ├── setupTests.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── App.css │ ├── App.js │ ├── logo.svg │ └── serviceWorker.js ├── config-overrides.js ├── .gitignore ├── package.json └── README.md └── README.md /cra-monaco/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /cra-monaco/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpavlenko/web-2020/HEAD/cra-monaco/public/favicon.ico -------------------------------------------------------------------------------- /cra-monaco/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpavlenko/web-2020/HEAD/cra-monaco/public/logo192.png -------------------------------------------------------------------------------- /cra-monaco/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpavlenko/web-2020/HEAD/cra-monaco/public/logo512.png -------------------------------------------------------------------------------- /cra-monaco/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /cra-monaco/config-overrides.js: -------------------------------------------------------------------------------- 1 | const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); 2 | 3 | module.exports = function override(config, env) { 4 | config.plugins.push( 5 | new MonacoWebpackPlugin({ 6 | languages: ["javascript"] 7 | }) 8 | ); 9 | return config; 10 | }; 11 | -------------------------------------------------------------------------------- /cra-monaco/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /cra-monaco/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /cra-monaco/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /cra-monaco/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /cra-monaco/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /cra-monaco/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cra-monaco/src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import MonacoEditor from "react-monaco-editor"; 3 | import "./App.css"; 4 | 5 | class App extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | code: "// type your code..." 10 | }; 11 | } 12 | editorDidMount(editor, monaco) { 13 | console.log("editorDidMount", editor); 14 | editor.focus(); 15 | } 16 | onChange(newValue, e) { 17 | console.log("onChange", newValue, e); 18 | } 19 | render() { 20 | const code = this.state.code; 21 | const options = { 22 | selectOnLineNumbers: true 23 | }; 24 | return ( 25 | 35 | ); 36 | } 37 | } 38 | 39 | export default App; 40 | -------------------------------------------------------------------------------- /cra-monaco/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cra-monaco", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "monaco-editor": "^0.19.3", 10 | "monaco-editor-webpack-plugin": "^1.8.2", 11 | "react": "^16.12.0", 12 | "react-dom": "^16.12.0", 13 | "react-monaco-editor": "^0.33.0", 14 | "react-scripts": "3.3.1" 15 | }, 16 | "scripts": { 17 | "start": "react-app-rewired start", 18 | "build": "react-app-rewired build", 19 | "test": "react-app-rewired test", 20 | "eject": "react-app-rewired eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": "react-app" 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.2%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | }, 37 | "devDependencies": { 38 | "copy-webpack-plugin": "^5.1.1", 39 | "react-app-rewired": "^2.1.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /cra-monaco/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /cra-monaco/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /cra-monaco/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `yarn start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `yarn test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `yarn build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `yarn eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `yarn build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /cra-monaco/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' } 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready.then(registration => { 134 | registration.unregister(); 135 | }); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Below are my thoughts on how I would pick a stack for a web development in 2020. I value mature technologies, size of the community, "batteries included" and speed of the development. 2 | 3 | I personally use VSCode editor, again because it's very popular these days which leads to lots of problems being solved in form of questions asked, plugins shipped and bugs fixed. One decent alternative is PyCharm/WebStorm. 4 | 5 | - [Frontend](#frontend) 6 | - [Frontend framework [React with Hooks]](#frontend-framework-react-with-hooks) 7 | - [Bundler config and boilerplate [Create React App]](#bundler-config-and-boilerplate-create-react-app) 8 | - [CSS-in-JS library [styled-components]](#css-in-js-library-styled-components) 9 | - [Components library [Ant]](#components-library-ant) 10 | - [Frontend state management [React Hooks -> Redux + Redux Toolkit + Redux Thunk (+ Redux Saga)]](#frontend-state-management-react-hooks---redux--redux-toolkit--redux-thunk--redux-saga) 11 | - [Router [React Router]](#router-react-router) 12 | - [Code editor as a component [Monaco]](#code-editor-as-a-component-monaco) 13 | - [Mobile development [React Native]](#mobile-development-react-native) 14 | - [Language flavor/safety [TypeScript]](#language-flavorsafety-typescript) 15 | - [Package managers [Yarn 1.22 or npm, npx]](#package-managers-yarn-122-or-npm-npx) 16 | - [Linters and formatters [ESLint/Prettier, Husky]](#linters-and-formatters-eslintprettier-husky) 17 | - [Backend](#backend) 18 | - [Backend framework [Django]](#backend-framework-django) 19 | - [API design [Django REST Framework]](#api-design-django-rest-framework) 20 | - [Authentication [?]](#authentication-) 21 | - [Language flavor/safety [Mypy]](#language-flavorsafety-mypy) 22 | - [Package managers [Pipenv]](#package-managers-pipenv) 23 | - [Linters and formatters [Black, flake8, isort]](#linters-and-formatters-black-flake8-isort) 24 | - [DevOps](#devops) 25 | - [Frontend deployment [Zeit Now]](#frontend-deployment-zeit-now) 26 | - [Database [Postgres]](#database-postgres) 27 | - [Backend deployment [DigitalOcean Docker (+Portainer) + Managed Postgres + CloudFlare]](#backend-deployment-digitalocean-docker-portainer--managed-postgres--cloudflare) 28 | - [Linters and formatters [ShellCheck for .sh, hadolint for Dockerfile]](#linters-and-formatters-shellcheck-for-sh-hadolint-for-dockerfile) 29 | - [Bonus track: Zsh tools](#bonus-track-zsh-tools) 30 | 31 | 32 | Frontend 33 | === 34 | 35 | Frontend framework [React with Hooks] 36 | --- 37 | 38 | React or Vue? 39 | 40 | Why switching from React (the de-facto standard and #1) if Vue has similar metrics and less ecosystem? For instance, Vue has no mainstream Monaco wrapper. Also, React Native is fun and mature. 41 | 42 | If you haven't worked with React since React Hooks emerged, you should totally catch up on that: 43 | - https://nikgraf.github.io/react-hooks/ 44 | - https://github.com/enaqx/awesome-react#react-hooks 45 | 46 | Other things to look at: 47 | - https://github.com/welldone-software/why-did-you-render 48 | 49 | 50 | 51 | Bundler config and boilerplate [Create React App] 52 | --- 53 | Webpack? Parcel? Create React App? Next.js? Gatsby? 54 | 55 | Nano React App: https://hackernoon.com/create-react-app-is-way-too-bloated-5db07c3511 56 | 57 | If project can't benefit from server-side rendering or SEO or static generation, then it's not worth going the Next.js or Gatsby way - less popular meaning less support, more traps. Sticking with CRA / ejected CRA. 58 | 59 | Parcel may be good, but I don't know if it's easy to wire Parcel and Monaco, as well as any other dependencies. Sticking with CRA. 60 | 61 | 62 | 63 | CSS-in-JS library [styled-components] 64 | --- 65 | Anything competing with styled-components? CSS Modules? Not worth it. 66 | 67 | Also, look at https://styled-system.com/ 68 | 69 | https://styled-components.com/docs/tooling#babel-macro 70 | 71 | 72 | 73 | Components library [Ant] 74 | --- 75 | Bootstrap? Material UI? Ant? 76 | 77 | https://ant.design/docs/react/use-in-typescript 78 | 79 | Ant has better TypeScript support and is being very actively developed 80 | 81 | To use: 82 | - https://github.com/fi3ework/vscode-antd-rush 83 | - https://marketplace.visualstudio.com/items?itemName=bang.antd-snippets 84 | - For inspiration: http://antd-admin.zuiidea.com/ 85 | - And in general: https://github.com/websemantics/awesome-ant-design 86 | 87 | For forms it makes sense to look into both Formik and Yup: 88 | - https://blog.bitsrc.io/creating-forms-in-react-with-formik-and-yup-698d09363a22 89 | - https://github.com/jannikbuschke/formik-antd 90 | 91 | 92 | 93 | Frontend state management [React Hooks -> Redux + Redux Toolkit + Redux Thunk (+ Redux Saga)] 94 | --- 95 | 96 | Storing the state inside react components should be enough for the start. Once there are significant pain points 97 | and the transition to Redux is inavoidable, we can start using Redux. Redux Toolkit is a modern official way to standardize the Redux boilerplate. And, in case there's going to be some nightmare complex multi-staged interaction back and forth 98 | 99 | To better understand the whole necessity of such a complex system like Redux, you may want to meditate on Storeon: 100 | - https://github.com/storeon/storeon 101 | 102 | To read: 103 | - https://redux-toolkit.js.org/introduction/quick-start 104 | 105 | 106 | 107 | Router [React Router] 108 | --- 109 | @reach/router vs. React Router 110 | 111 | @reach/router's core feature with auto focus is actually very annoying and inavoidable by design. 112 | While it may make things more accessible, it makes a hell of auto scrolling. 113 | 114 | Features from @reach/router are slowly backported to React Router anyways. Also, not a huge difference between the two libraries, 115 | so it shouldn't be painful to switch back and forth. You may start with @reach/router and switch once you hit this auto focus trap or need more customization. 116 | 117 | - https://reacttraining.com/blog/reach-react-router-future/ 118 | - https://github.com/reach/router/blob/master/website/src/markdown/pages/typescript.md 119 | 120 | 121 | 122 | Code editor as a component [Monaco] 123 | --- 124 | https://habr.com/ru/company/Voximplant/blog/445390/ 125 | 126 | Monaco is compatible with pre-eject CRA: https://github.com/react-monaco-editor/react-monaco-editor/issues/263 127 | See [cra-monaco/](cra-monaco). Although this isn't maintained: https://medium.com/@kitze/configure-create-react-app-without-ejecting-d8450e96196a 128 | 129 | 130 | 131 | Mobile development [React Native] 132 | --- 133 | 134 | React Native, Code Push, Fastlane. 135 | 136 | Flutter is good but you already have React, so why make your stack more complex. 137 | 138 | Unless you build games or rely on bleeding edge APIs like VR/AR, you don't need native Swift/Kotlin development. It's more technologies => more engineering effort and cost, in terms of hours and team size and team salary and communication. 139 | 140 | Examples of large React Native projects: 141 | - https://github.com/zulip/zulip-mobile 142 | - https://github.com/mattermost/mattermost-mobile 143 | 144 | 145 | 146 | Language flavor/safety [TypeScript] 147 | --- 148 | Flow or Typescript? 149 | 150 | Looks like Flow is on par with TypeScript and it's better with React - both are maintained by Facebook. One may also migrate from Flow to TypeScript later on: https://medium.com/inato/migrating-from-flow-to-typescript-why-how-worth-it-5b7703d12089 151 | 152 | Flow is often blamed to have bad VSCode support and random bugs. Yarn and Jest migrated from Flow to TypeScript. Same for React Native's Expo. 153 | 154 | No need to use TSLint anymore: https://medium.com/palantir/tslint-in-2019-1a144c2317a9 155 | 156 | Looks like CRA doesn't have ES2016 support, let alone ES2020. What exactly are we missing: https://github.com/tc39/proposals/blob/master/finished-proposals.md It might make sense to enable it using customize-cra: https://2muchcoffee.com/blog/es7-decorators-how-to-use-the-decorator-syntax-in-react/ 157 | 158 | Have a look at: 159 | - https://github.com/dzharii/awesome-typescript 160 | - https://github.com/typescript-cheatsheets/react-typescript-cheatsheet 161 | - https://github.com/jeffijoe/typesync 162 | - https://mariusschulz.com/blog/series/typescript-evolution 163 | 164 | 165 | 166 | Package managers [Yarn 1.22 or npm, npx] 167 | --- 168 | Yarn 2 can fail to work with react-app-rewired, so sticking to a Yarn 1.* like 1.22 is a safe option for now.s 169 | 170 | It looks like npm is being actively developed and is on par with Yarn these days, whereas Yarn has almost no big changes throughout 2019. 171 | 172 | - https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b 173 | 174 | 175 | 176 | Linters and formatters [ESLint/Prettier, Husky] 177 | --- 178 | 179 | ESLint/Prettier with typescript-eslint. Pre-commit hooks running necessary tools with Husky. 180 | - https://prettier.io/docs/en/integrating-with-linters.html 181 | - https://github.com/alexgorbatchev/eslint-import-resolver-typescript 182 | - https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project 183 | - https://dev.to/botreetechnologies/setting-up-husky-pre-commit-hook-with-eslint-prettier-and-lint-staged-for-react-and-react-native-d05 184 | 185 | 186 | 187 | Backend 188 | === 189 | 190 | Backend framework [Django] 191 | --- 192 | 193 | Django or Express.js? 194 | 195 | First of all, there are tons of web frameworks, and all popular languages have several of them. 196 | The most popular ones are all pretty much equal in their maturity - if you compare Django to Rails to Laravel to ASP.NET to Play. 197 | So this is a choice of a language more than a choice of a framework - the framework should be 198 | "a single most popular for your language". 199 | 200 | Python is a winner in terms of language maturity and size of its community. 201 | It leads in all other applications - it has ML libraries, it has the support for any kind of data processing you may need to do. 202 | 203 | So the real question is, do you want to have two languages (JS + Python) in your project, or you'd rather just have one 204 | and stick to Node.js? 205 | 206 | You can assmeble a stack around Express.js. It's essential to use the RDBMS way for the main data storage (i.e. not to slip into the NoSQL trap), which leads to using sequelize as an ORM. And sequelize itself isn't as smooth as Django ORM - it requires more manual actions and verbose configuration. It was also hard to interact with using REPL due to async nature - maybe it's still hard. 207 | 208 | I love Django admin panel, Django ORM, migrations, shell, dbshell, Jupyter notebooks with Django support. 209 | It's very hard to assemble such a stack on Node.js. 210 | Also, sequelize sucks, and NoSQL as a main data storage sucks way more. So - Django. 211 | 212 | (Alternatives - async Flask equivalent is FastAPI. Why do you need an async framework though?) 213 | 214 | Django boilerplate - cookiecutter? 215 | - https://github.com/pydanny/cookiecutter-django 216 | - https://github.com/agconti/cookiecutter-django-rest 217 | - https://github.com/wsvincent/awesome-django#boilerplate 218 | 219 | Cool libraries: 220 | - https://django-extensions.readthedocs.io/ 221 | - https://django-extensions.readthedocs.io/en/latest/field_extensions.html 222 | - https://django-extensions.readthedocs.io/en/latest/shell_plus.html to run Jupyter Notebook 223 | - https://github.com/jazzband/django-debug-toolbar/ 224 | - https://github.com/arteria/django-hijack 225 | - https://github.com/wsvincent/awesome-django#models 226 | - https://github.com/joke2k/django-environ 227 | 228 | Open-source Django projects: 229 | - https://github.com/zulip/zulip/ with very detailed documentation https://zulip.readthedocs.io/ 230 | - https://github.com/taigaio/taiga-back 231 | - https://github.com/wagtail/wagtail/ 232 | - https://github.com/edx/edx-platform 233 | - https://github.com/django/djangoproject.com 234 | 235 | Blog posts: 236 | - https://vsupalov.com/django/ 237 | - https://vsupalov.com/quick-django-refresher-crash-course/ 238 | - https://vsupalov.com/django-custom-user-model/ 239 | - https://medium.com/3yourmind/keeping-django-database-migrations-backward-compatible-727820260dbb 240 | 241 | 242 | 243 | API design [Django REST Framework] 244 | --- 245 | 246 | Should we use REST or GraphQL? 247 | 248 | Django has a very mature REST framework - Django REST framework (DRF), used by 108k projects according to Github stats. 249 | It's been actively maintained since 2011 and very well documented. 250 | 251 | GraphQL is a new technology that gives the following benefits: 252 | - typed API requests: auto checking, no need to do Swagger/Postman knowledge sharing between frontend and backend teams 253 | - no overfetching/underfetching 254 | 255 | While the technology itself is cool, it's way more supported in the Node.js world, where the express-graphql library 256 | has same 109k users. It's support in the Django world is young and limited: graphene_django has 3k users, it's poorly documented, not very actively maintained and has performance issues. 257 | 258 | So for now, Django should be used with DRF. If GraphQL is a must, one should entirely switch to the Node.js stack. 259 | 260 | On GraphQL: 261 | - https://github.com/Shopify/graphql-design-tutorial/blob/master/TUTORIAL.md 262 | - https://www.howtographql.com/ 263 | 264 | On current GraphQL support for Django: 265 | - https://news.ycombinator.com/item?id=20200203 266 | - https://yeti.co/blog/migrating-from-rest-to-graphql-in-django/ 267 | 268 | On DRF: 269 | - https://www.valentinog.com/blog/drf/ 270 | - https://github.com/wsvincent/awesome-django#django-rest-framework 271 | 272 | 273 | Authentication [?] 274 | --- 275 | 276 | Question 1. How to authenticate API requests once the user is logged in? 277 | 278 | Options: sessions/cookies, JWT tokens, some other tokens. 279 | 280 | Sessions may be hard to work with mobile clients. 281 | 282 | - https://fractalideas.com/blog/making-react-and-django-play-well-together/ 283 | - https://www.django-rest-framework.org/api-guide/authentication/ 284 | - https://developer.okta.com/blog/2017/08/17/why-jwts-suck-as-session-tokens 285 | - http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/ 286 | - https://afdezl.github.io/post/authentication-react-native-django-1/ 287 | 288 | Question 2. How to implement social authentication and allow user to sign in / sign up using Google/Facebook? 289 | 290 | Implementing social authentication is still hard. You can either rely on third party 291 | services or just plug in some libraries. Third party services like Amazon Cognito or Auth0 292 | usually cost money because that's how they make money. The possible upside of using them is that they keep 293 | integrations up-to-date. Every year or so your oauth providers like Google or Facebook change their protocol 294 | or bump their API version, and your previous sign-in button can become deprecated and broken. 295 | (Happened to me several times.) 296 | 297 | If you use your own libraries, you don't pay but you sometimes need to do integration maintenance. Plus, 298 | the existing libraries aren't really that popular or smooth. 299 | 300 | For Node.js worls, Looks like Passport.js is huge and popular. 301 | 302 | Can do plain django.contrib.auth with native pages (i.e. no React wrappers around, just redirects) - for prototyping. 303 | 304 | There is python-social-auth and Django_allauth and django-rest-auth. The latter isn't supported anymore, but it's required to wire with DRF. Also it's maybe not that painful because it's a tiny layer. No critial Issues found at their bug tracker. 305 | 306 | Google One-Tap (Google YOLO) is a great experience, but it's still not public. 307 | 308 | 309 | 310 | Language flavor/safety [Mypy] 311 | --- 312 | 313 | Should we use typed Python? 314 | https://blogs.dropbox.com/tech/2019/09/our-journey-to-type-checking-4-million-lines-of-python/ 315 | 316 | Mypy is worth a try - the code should be clearer. Once I start doing Mypy - follow this guide: https://realpython.com/python-type-checking/ 317 | 318 | Django-stubs is Django with types: https://sobolevn.me/2019/08/typechecking-django-and-drf 319 | 320 | Looks like Pyright (Microsoft) and Pyre (Facebook) are both maintained tools that do Mypy-like type checking, but faster. 321 | 322 | 323 | 324 | Package managers [Pipenv] 325 | --- 326 | 327 | Pipenv is a modern replacement for pip/virtualenv, although it itself isn't being actively supported since Oct 2018. 328 | Poetry isn't as mature as pipenv - eg. [pipenv is working in VSCode with zero configuration](https://code.visualstudio.com/docs/python/environments) 329 | 330 | - https://gioele.io/pyenv-pipenv 331 | - https://xkcd.com/1987/ 332 | 333 | 334 | 335 | Linters and formatters [Black, flake8, isort] 336 | --- 337 | 338 | - https://github.com/vintasoftware/python-linters-and-code-analysis (also watch out for Django-specific linters) 339 | - https://github.com/DmytroLitvinov/awesome-flake8-extensions 340 | - https://github.com/vinta/awesome-python#code-analysis 341 | - https://wemake-python-stylegui.de/en/latest/pages/usage/integrations/auto-formatters.html 342 | - http://www.locallyoptimal.com/blog/2019/08/23/why-you-should-use-black-for-your-python-style-linting/ 343 | - https://github.com/psf/black/issues/333#issuecomment-516088368 344 | - https://dmerej.info/blog/post/bye-bye-pylint/ 345 | - https://github.com/pilat/vscode-importmagic 346 | 347 | What's suspicious about https://github.com/wemake-services/wemake-django-template ? 348 | - poetry instead of pipenv 349 | - new Caddy instead of old proven nginx - it's not worth risking with this layer of infrastructure 350 | - Gitlab CI - not a default Github CI 351 | - wemake-python-styleguide is against black, and I love auto-formatters 352 | 353 | A sample project with Black, flake8, mypy and isort are configured to work together and with Django/DRF: https://github.com/vpavlenko/drf-tutorial 354 | 355 | 356 | 357 | DevOps 358 | === 359 | 360 | Frontend deployment [Zeit Now] 361 | --- 362 | 363 | Netlify or Zeit Now or Github Pages? 364 | 365 | Try Zeit Now and Netlify, in this order. 366 | 367 | Github Pages might be a weaker option because it's not a core business for the company => less convenient. 368 | 369 | On marrying frontend and backend: 370 | - https://fractalideas.com/blog/making-react-and-django-play-well-together/ 371 | - https://fractalideas.com/blog/making-react-and-django-play-well-together-single-page-app-model/ 372 | 373 | 374 | 375 | Database [Postgres] 376 | --- 377 | Postgres? (ok, at least here there's no alternative) 378 | 379 | Reasoning: I want an RDBMS because why disentangle denormalized Mongo crap if you only have one life? Postgres is top-1 RDBMS. 380 | 381 | 382 | 383 | Backend deployment [DigitalOcean Docker (+Portainer) + Managed Postgres + CloudFlare] 384 | --- 385 | Amazon/GCP/Azure? Heroku? VPS/Docker/Lambda? RDS? 386 | 387 | Heroku isn't great - need to learn + tricky pricing. 388 | 389 | DigitalOcean VPS may be good, although there's no autoscaling and Postgres-specific backups. 390 | 391 | Dev db, dev env? 392 | 393 | No Lambda - hard to reason about it given my experience with VPS and the whole Django entirety. 394 | 395 | Let's try Docker. 396 | 397 | uWSGI / gunicorn - both aren't well supported, but gunicorn had more patches over the last years. 398 | 399 | DigitalOcean has its own RDS competitor: https://www.digitalocean.com/products/managed-databases/ 400 | [Loss in latency (100ms)](https://levelup.gitconnected.com/performance-aws-rds-postgres-vs-digital-ocean-postgres-8c2500197f1c), better documentation. (DigitalOcean also has its own S3) 401 | 402 | Kubernetes / Docker Swarm is probably an overkill because one instance with Docker Compose should be enough. 403 | 404 | So, DigitalOcean, Django app living in a docker, ability to add a load balancer and more dockers, 405 | and a Managed Database (Postgres) with backups. 406 | 407 | [Horizontal scaling for the Django part is better than vertical](https://coderbook.com/@marcus/how-scalable-are-websites-built-in-django-framework/) 408 | 409 | Why doesn't googling "django pgbouncer" yield a lot of results? https://stackoverflow.com/questions/40248970/django-settings-when-using-pgbouncer 410 | 411 | CloudFlare from the start: for hiding the real server API, maybe caching something, DNS configuration, protecting from malicious 412 | DDoS (they happen even at a small scale!). 413 | 414 | Monitoring: Sentry, Datadog? 415 | 416 | - https://vsupalov.com/same-docker-container-django-postgresql/ and all other articles on vsupalov.com 417 | - https://vsupalov.com/tools/portainer/ 418 | - https://vsupalov.com/speed-up-python-docker-image-build/ 419 | - https://vsupalov.com/deploying-like-a-startup/ 420 | 421 | 422 | 423 | Linters and formatters [ShellCheck for .sh, hadolint for Dockerfile] 424 | --- 425 | 426 | - https://github.com/koalaman/shellcheck 427 | - https://github.com/hadolint/hadolint 428 | 429 | 430 | 431 | Bonus track: Zsh tools 432 | --- 433 | 434 | - https://github.com/ohmyzsh/ohmyzsh 435 | - https://denysdovhan.com/spaceship-prompt/ 436 | - https://github.com/sharkdp/bat 437 | - https://github.com/ogham/exa 438 | - https://github.com/ggreer/the_silver_searcher 439 | - https://sobolevn.me/2017/10/using-better-clis 440 | - https://sobolevn.me/2017/08/instant-command-line-productivity 441 | --------------------------------------------------------------------------------