středa 28. června 2017

Desktop aplikace v javascriptu

Od doby, kdy Brendan Eich vytvořil svou první verzi javascriptu, uběhlo již více než 21 let. Přesněji řečeno, na podzim bude slavit dvaadvacáté narozeniny. V té době se jednalo o jazyk, který měl být "lehčí" variantou Javy a proto i zvolený název se nese v duchu snahy, se svést na popularitě Javy.


V roce 1995 byl svět naprosto rozdílný. Alespoň co se IT technologií týče. V té době neexistovaly sociální sítě a o mobilních telefonech jste si mohli nechat leda tak zdát. Byli jste rádi, že jste sehnali Dooma na disketách a pomocí ladění konvenční paměti, ho nakonec rozeběhli. Bavíme se o době, kdy počítače hrály naprostou jinou roli a byly spíše výsadou několika nadšenců.

Asi nikdo by tenkrát nevsadil na to, že se javascript dostane do fáze, ve které ho známe dnes. Z nechtěného dítěte v podobě věcí jako jQuery, po pubertu v podobě Node.js až k dospělosti v podobě cloud lambda functions a serverless architektury.

Dnešní článek bude pojednávat o tom, jak lze javascript využít pro desktop aplikace.

Kdy je dobré přemýšlet o javascriptu na desktopu?

Pokud se rozhodnete, že javascript zvolíte pro desktop aplikaci, měli byste si dobře rozmyslet, zda je to pro vaši budoucí aplikaci vhodná technologie.

Abych se vyhnul obecným pravidlům, kdy ano a kdy ne, zkusím popsat důvody, proč jsme se touto cestou rozhodli jít my.

Zkusme si to v rychlosti shrnout....

Pracuji na projektu multitenantní cloud aplikace s webovým a mobilním klientem. Cílem aplikace je sbírání dat z různých bluetooth zařízení a tyto data ukládat zpět do cloudu. Na straně serveru je javascript v podobě node.js, express či graphql a na straně klienta je to React pro web a React Native pro mobilní aplikaci. Zjednodušeně řečeno, vše je javascript (v našem případě s pomocí Typescriptu).

Existují dva hlavní důvody, proč je nutné napsat desktop aplikaci. 

Prvním důvodem je to, že v případě komunikace Android vs Bluetooth LE se často dostanete do neřešitelných problémů v podobě různých verzí Androidu, různých výrobců jednotlivých zařízení a také v podobě toho, že zjistíte, že s určitým BT zařízením nejste schopni komunikovat, protože to prostě a jednoduše nejde. Existuje zde ještě možnost komunikace přes novou verzi Chrome, který Bluetooth LE sice umí, ale je to zatím tak nestabilní, že na produkční nasazení je příliš brzy.

Druhý důvod je ten, že existují zákazníci, kteří požadují desktop aplikaci, protože mobilní verze je pro ně nevhodná.

Cílová aplikace by tedy měla mít následující vlastnosti:
  1. Možnost komunikace s Bluetooth LE
  2. Komunikace s cloud serverem
  3. Automatické aktualizace celé aplikace v podobě centrálního řízení z cloudu
  4. Aplikace by měla být multiplatformní (Windows, Linux, Mac OS)
Dá se říci, že se jedná o vcelku jednoduchou aplikaci.

Jak už jsem zmínil výše, zbytek systému je napsán v javascriptu, takže jsem se dostal na rozcestí, zda jít doprava či doleva. Na jedné straně by bylo možné napsat aplikaci v Jave, ve které jsem kdysi už jednu aplikaci psal a mám s tím zkušenosti. Na straně druhé, jít cestou javascriptu, díky kterému by nebylo nutné zavádět další technologii či jazyk.

Jak už článek napovídá, volba padla na javascript a to přesně z těchto důvodů:
  1. Díky jednotnému jazyku se snižuje komplexita systému
  2. Možnost sdílení kódu mezi jednotlivými částmi systému
  3. Využití stejného paradigmatu jako v případě webu a mobilní aplikace
  4. Jednoduchá distribuce a aktualizace, například pomocí Code Push
  5. Aplikace může být multiplatformní
Zkráceně řečeno, pro potřeby naší aplikace je javascript dobrou volbou.

V čem tedy takovou aplikaci přesně napsat?

Pokud se podíváte, jaké máte možnosti, tak vám nakonec zbydou dvě varianty. Electron a NW.js.

Volba padla na Electron a to čistě z pragmatických důvodů. Vím, že by pro naše potřeby bylo možné jí i druhou cestou a to vlastně i z toho důvodu, že Electron je jen abstrakce nad NW.js.

Nicméně Electron je populárnější a jsou v něm napsány aplikace, které již znám. Za všechny můžu zmínit třeba Visual Studio Code, Atom či Slack. Dobrá reference, která zajišťuje to, že projekt za měsíc "neumře" :)

Jak Electron funguje?

Electron je rozdělen na dvě hlavní části. První částí je main process, který běží pod samotným Node.js a renderer fáze, která běží pod Chromiem.

Velice zjednodušeně je to tak, že při spuštění aplikace se pustí main proces, který otevře okno, ve kterém je renderer část.

Main process

Hlavní a spouštěcí proces beží pod Node.js, které vám umožňuje přistupovat k samotnému "železu" hostitele. Tedy máte možnost pracovat s file systémem či třeba s právě zmíněným bluetooth adaptérem.

V main procesu dále nastavujete například velikost okna či modifikujete samotné menu.

Renderer část

K renderer části se toho moc psát nemusí. Jedná se v podstatě o webovou stránku. Tedy máte možnost použít vše, na co jste zvyklý z webových aplikací. V našem případě byla volba jasná a tou je React + Redux. Takže díky tomu, nejen, že člověk píše stejným způsobem, ale také má možnost vetšinu svého kódu sdílet. Díky různým responsivním CSS frameworkům jako je Bootstrap, je možné i prvky správně pozicovat. V tomto případě je možná i lepší variantou jít do čistého CSS flexboxu a tím pozicovat prvky tak, jak je člověk zvyklý z jiných jazyků, ve kterých se běžně desktop aplikace píší.

Co se týče samotného Reduxu, tak jeho State může být uložen v localStore či přímo na disku ve vlastním souboru. To může pomáhat tomu, aby bylo možné, napsat aplikaci s offline variantou.

Komunikace mezi main a renderer

Electron má pro komunikaci mezi těmito procesy vlastní řešení. Tím řešením je ipcMain a ipcRenderer. A jak už bývá zvykem, tato komunikace může být synchronní i asynchronní.

Jednoduchá ukázka komunikace mezi main a renderer:
// main process
import {ipcMain} from 'electron';
ipcMain.on('hello', () => console.log('Hello world'));

// renderer
import {ipcRenderer} from 'electron';
ipcRenderer.send('hello');

Zprávy můžete samozřejme posílat obousměrně a tím zajistit kompletní integraci.

Závěr

Co se týče samotného stacku, který jsme pro desktop aplikaci zvolili, tak je vcelku jednoduchý a vlastně stejný jako v případě webu. Tedy: Typescript, Webpack, React a Redux.

I přes to, že jde o vcelku moderní způsob, tak je neodiskutovatelné, že pro produkční nasazení se jedná o naprosto relevantní variantu. Jádro samotné aplikace totiž leží v renderer části, která je dnes a denně testována stovkami miliónů uživatelů. Proto není důvod se bát toho, že by javascript nebyl na desktopu použitelný. Osobně se domnívám, že existuje velké množství aplikací, které mohou být v javascriptu napsány.

O tom, jak to bude v budoucnu, se nechme překvapit. Nicméně jedno je jisté, desktop aplikací v javascriptu bude jen přibývat. A to je dobře :)

pondělí 26. června 2017

React a Typescript podruhé - proklaté this

V předchozím článku jsem ukázal jak používat Typescript v Reactu. Dneska se pojďme podívat na zbylou část, která se bude motat kolem klíčového slova this.

Javascript a this

Spoléhání na this v javascriptu, je stejné, jako hrát ruskou ruletu. Do funkce lze totiž ono this podstrčit a tím změnit kontext. Do toho se ještě zamíchá function.bind, use strict a arrow functions a už z toho máme slušný guláš.

Dnešní článek se bude snažit vám odhalit, jak to s tím samotným this vlastně je a proč je v JSX bind(this) tou nejhorší možnou volbou.

Funkce a this

Lepší, než tisíc slov, je vždy malá ukázka. Pokud máme následující kód, zkuste uhádnout, co bude jeho výsledkem:
function test() {
    return this;
}
console.log(test());

Pokud tento příklad spustíte ve webu, bude dané this obsahovat globální objekt window. Pokud daný kód spustíte v node.js, bude obsahovat globální objekt global. O tom, co je globální objekt, se můžete dočíst zde.

Nyní pojďme náš kód trochu upravit:
function test() {
    'use strict';
    return this;
}
console.log(test());

V současné chvíli bude this undefined. Proč tomu tak je? Důvodem je přidání strict módu. Zjednodušeně se dá říci, že strict mód nám modifikuje chování javascriptu. Tento strict mod vznikl v ES5 a pokud vás zajímá, co všechno modifikuje, můžete si danou věc pročíst zde.

Pojďme si ukázat další variantu:
function test() {
    return this;
}
console.log(test.bind({invasion:'Hello'})());

Výsledkem bude, že this bude obsahovat podstrčený objekt {invasion: 'Hello'}. Toto je přesně ta ukázka, kterou ve svém projektu nechceme používat, ač je to častý jev. Důvod proč, rozeberu v následující ukázce:
import * as React from 'react';

interface Props {
    onClick: (origin: string) => void;
}

export class Button extends React.Component<Props, void> {

    handleOnClick() {
        this.props.onClick('From button component');
    }
    
    render() {
        return (
            <button onClick={this.handleOnClick.bind(this)}>Click me!</button>
        )
    }
}

Co zde vidíme? Máme komponentu s názvem Button, která vrací callback s názvem onClick. Pokud bychom v JSX nepoužili this.handleOnClick.bind(this), tak by naše funkce nemohla použít this.props.onClick(origin). Jak jsme si ukázali výše, tak this je ve funkci buď globální objekt, nebo undefined. Podle toho, zda jsme v režimu strict či nikoli.
Ono by na tomto příkladě nic tak špatného nebylo, až na to, že pokud si uvědomíme, jak funguje React a metoda render, tak zjistíme, že jsme napsali dost neefektivní kód. Metoda render se totiž bude vykonávat velice často a to vždy, když je komponenta poprvé renderována a také ve chvíli, kdy jí předek změní props či se modifikuje state. To od Reactu přeci očekáváme, že takto bude fungovat. Nicméně v případě metody bind se dostáváme k tomu, že vlastně znovu a znovu a znovu vytváříme další funkci. Finále je, že jen dost zatěžujeme Garbage Collector, který musí znovu a znovu zahazovat něco, co jsme vytvořili, aniž bychom o tom věděli.

Jak z toho ven?

První variantou je to, že bind aplikujeme v konstruktoru. Je to efektivní způsob, jak se vyhnout tomu, aby nám nevznikalo příliš funkcí, ale také to je způsob, který bych označil spíše za nedoporučovaný. Důvod proč, je zřejmý. V případě nové handler funkce musíte provést její registraci v konstruktoru.

Pojďme se podívat na ukázku:
import * as React from 'react';

interface Props {
    onClick: (origin: string) => void;
}

export class Button extends React.Component<Props, void> {

    constructor(props: Props, context: any) {
        super(props, context);
        this.handleOnClick = this.handleOnClick.bind(this)
    }

    handleOnClick() {
        this.props.onClick('From button component');
    }

    render() {
        return (
            <button onClick={this.handleOnClick}>Click me!</button>
        )
    }
}

Paráda. Sice jsme se zbavili bind v JSX, ale že by to byla taková výhra se říct nedá.

Druhou a lepší variantou jsou arrow functions. Než se dostaneme k výslednému řešení, pojďme se podívat jak to je s this v arrow functions.

Arrow functions a this

Arrow functions byly přidány v ES6 a dá se říci, že díky nim se nám více přiblížilo funkcionální programování. Arrow functions zjednodušují zápis a také mají jednu důležitou vlastnost a tou je právě zmíněné this.

Pojďme se podívat na ukázku:
const test = () => this;
console.log(test());

Skvěle. Máme stejnou funkci test, akorát přepsanou pomocí arrow function. Co bude jejím výsledkem? Bude to prázdný objekt {}. Skvělé na zamotání hlavy :)

Arrow functions mají v this současný kontext. Abychom to lépe pochopili, pojďme se podívat ještě na jednu ukázku:
this.user = {
    id: 1,
    firstName: 'Ales',
    lastName: 'Dostal',
};
const test = () => this;
console.log(test());

Výsledkem bude, že this obsahuje objekt s atributem user.

Nyní si pojďme tuto znalost aplikovat na React komponentu user.

Výsledkem bude následující kód:
import * as React from 'react';

interface Props {
    onClick: (origin: string) => void;
}

export class Button extends React.Component<Props, void> {

    handleOnClick = () => {
        this.props.onClick('From button component');
    };

    render() {
        return (
            <button onClick={this.handleOnClick}>Click me!</button>
        );
    }
}

Díky arrow functions již nepotřebujeme ani konstruktor, ani funkci bind.

Závěr

O this v javascriptu je napsáno spoustu článků. Existují i nové návrhy, které by mohly více přispět tomu, abychom se samotnému bind vyhnuli. Asi nejblíže tomu je samotný návrh pomocí dokorátoru @autobind, což jsou v podstatě metadata nad funkcemi, které určují, jak se budou v daném kontextu chovat. Jestli budou dekorátory, které se v nové ECMA specifikaci připravují, tou správnou volbou, to ukáže čas.

pátek 16. června 2017

React a Typescript

V poslední době se mě několik lidí ptalo, jestli píši v čistém javascriptu, nebo používám něco jiného. Odpověď je jednoduchá: "Mým hlavním jazykem je Typescript". Důvody proč právě Typescript, jsem zmiňoval v jednom z předešlých článků.

Dnes bych se chtěl zaměřit na to, jak vlastně využít Typescript s Reactem. Dnešní článek bude více zaměřen na jednotlivé ukázky, než na teoretickou část.

Kde začít?


V první řadě je nutné říci, že díky tomu, že React je Facebook technologie, primárně své ukázky uvádí buď v čistém javascriptu, nebo pomocí Flow. Důvod, proč právě Flow je zřejmý, je to technologie, která je také od Facebooku.

V té chvíli nastává problém, kde přesně zjistit, jak používat Typescript. Nezbývá tedy nic jiného, než že projdete několik blogů a ukázek na githubu.

Abych vám ušetřil čas, který byste museli trávit při hledání "správného řešení", zkusím projít jednotlivé části, na kterých ukážu, jak Typescript v Reactu použít. V případě ukázky v čistém javascriptu, bude použita ES6 specifikace.

Pojďme tedy na to....

Functional Stateless Components


První, na co se zaměřím jsou stateless komponenty. Jinými slovy, jsou to komponenty, které jsou v podstatě funkcí a neobsahují ani state, ani možnost využítí lifecycle. Jediné, co máte k dispozici jsou props.

React Stateless componenty byly představeny ve verzi 0.14 a veřte, že by vaše aplikace měla být složena z 95%, právě těmito komponentami.

Javascript - Stateless component
import React from 'react';

const HelloWorld = ({name}) => (
    <div>{`My name is ${name}`}</div>
);

export default HelloWorld;

Typescript - Stateless component
import * as React from 'react';

interface Props {
    readonly name: string;
}

export const HelloWorld: React.SFC<Props> = ({name}) => (
    <div>{`My name is ${name}`}</div>
);

První, čeho si můžete všimnout je import. Tato drobná změna není až tak důležitá. Stačí se podívat, jakým způsobem se importuje v Typescriptu a bude vám to jasné.

Druhou věcí je interface. To už je zajímavější část. Důvod, proč definujeme interface jakožto datový typ pro props, je ten, abychom měli danou komponentu "typově pod kontrolou". V rámci definice tohoto typu si můžete všimnout i klíčového slova "readonly". Osobně toto klíčové slovo vnímám jako jednu z ohromných výhod Typescriptu a umožňuje mi nastavovat, že daný atribut je immutable. A pokud víte, jak pracuje props v React komponentách, tak je to přesně vlastnost, kterou chcete využít. Dá se říci, že téměř všechny atributy v typech označuji tímto klíčovým slovem. Immutable stav je přesně to, co v Reactu chceme mít.

Další věcí je export. Sice máte možnost i Typescriptu použít export default, ale nedělejte to. Důvod proč, je vcelku jednoduchý. V případě, že použiji export default, tak v souboru, kam importuji tuto komponentu nemám pod kontrolou její název. Poté se dost zesložiťuje nejen refactoring kódu, ale také i případné dohledávání jednotlivých vazeb. Zjednodušeně řečeno, export default není zrovna dobrá volba.

Typescript - import with default
import NazevKteryJsemSiVymyslel from './HelloWorld';

Typescript - import without default
import {HelloWorld} from './HelloWorld';

Poslední věcí je definování typu pro React Stateless komponentu. K tomuto účelu existuje právě zmíněný deklarovaný typ SFC. Jde o zkratku "StatelessComponent", která je v React typové definici také. Osobně spíše využívám zkratku SFC, která mi šetří místo, které mohu využít pro výčet atributů z props přes destructuring assignment.

Stateful components


Druhou variantou jsou stateful komponenty. Tyto komponenty je vhodné využívat v případě kontejnerů, tedy komponent, které jsou napojeny na Redux. Dalším případem, kdy má stateful komponenta využití, je, pokud potřebujeme state či lifecycle.

Javascript - Stateful component
import React from 'react';

class HelloWorld extends React.Component {

    render() {
        return (
            <div>Hello world!</div>
        );
    }
}

export default HelloWorld;

Typescript - Stateful component
import * as React from 'react';

interface Props {

}

export class HelloWorld extends React.Component<Props, void> {

    render() {
        return (
            <div>Hello world!</div>
        );
    }
}

V tomto jednoduchém případě se příliš věcí nemění. Kromě již zmíněného importu je možné si všimnout, že díky Typescriptu mohu využít generiku, kde prvním parametrem jsou props a druhým state. V případě, že state nepoužíváte (což v případě Reduxu je požadovaný stav), je možné state nastavit na void.

Pojďme se podívat trochu dál....

Redux


Pokud ve své aplikaci používáte Redux, musíte splnit několik kroků, abyste Redux mohli používat. Nechci se zde zabývat tím, jak nastavit Redux, ale jak v rámci Reduxu používat Typescript.

První věc, kterou byste měli udělat, je vytvoření typové definice vašeho state. S největší pravděpodobností budete svojí aplikaci rozdělovat do menších celků, které na konci spojíte pomocí combineReducers.

Nejdříve si tedy navrhneme Redux state, který bude reprezentován následujícími typy. Pro zjednodušení je vše napsáno v jedné části, nicméně je dobré, aby kazdý interface byl ve vlastním souboru.

Typescript - Redux state
export interface UsersState {
    readonly list: User[];
    readonly isFetching: boolean;
    readonly lastFetched: Date;
}

export interface TenantsState {
    readonly list: Tenant[];
    readonly isFetching: boolean;
    readonly lastFetched: Date;
}

export interface State {
    readonly users: UsersState;
    readonly tenants: UsersState;
}

Další věcí jsou redux akce, které budeme chtít z komponent volat, aby modifikovaly redux pomocí reducerů. Definice akcí by mohla vypadat následovně:

Typescript - Redux actions
import {Dispatch} from 'redux';
import {State} from './State';
import {fetch} from 'tva-preferovana-knihovna';

const PREFIX = 'USERS_';

export const UsersActions = {
    FETCHING: `${PREFIX}FETCHING`,
    FETCHED: `${PREFIX}FETCHED`,
    fetchUsers() {
        return (dispatch: Dispatch<State>, getState: () => State) => {
            dispatch({type: UsersActions.FETCHING});
            fetch('Zde bude GraphQL dotaz - REST je mrtvy :)').then((result) => {
                dispatch({type: UsersActions.FETCHED, payload: result});
            }).catch((err) => {
                // Zpracovani chyby, klidne pres dalsi Redux akci
            });
        };
    },
};

Nyní mám vytvořenu akci fetchUsers(), která nejdříve odešle do reduceru akci, že se data nahrávají a poté v Promise callbacku provede druhou akci, která má již i payload, kde jsou stažena daná data.

Další část skládanky jsou samozřejmě reducery. V ukázce používám knihovnu redux-actions, kterou považuji za vhodnou, pokud se chcete vyhnout psaní pomocí switch-case.

Typescript - Reducers
import {handleActions} from 'redux-actions';
import {UsersState as State} from './UsersState';
import {UsersActions as Actions} from './UserActions';

const initialState = {
    list: [],
    isFetching: false,
    lastFetched: null,
} as State;

export const UsersReducer = handleActions<State, any>({

    [Actions.FETCHING]: (state: State): State => {
        return {...state, isFetching: true};
    },

    [Actions.FETCHED]: (state: State, action: Action<User[]>): State => {
        return {...state, isFetching: false, list: action.payload, lastFetched: new Date()};
    },

}, initialState);

Redux máme hotový. Teď už zbývá pouze daný Redux napojit na container.

React Containers


Jak už jsme si řekli, kontejnery jsou v podstatě React Stateful komponenty, které jsou napojené na Redux. Pojdmě si takový kontejner zkusit napsat a využít typovosti, kterou nám nabízí Typescript.

Typescript - React container
import * as React from 'react';
import {bindActionCreators, Dispatch} from 'redux';
import {connect} from 'react-redux';
import {State} from './State';
import {User} from './User';
import {Button} from './Button';
import {UsersList} from './UsersList';

interface OwnProps {
}

interface ConnectedState {
    readonly list: User[];
}

interface ConnectedDispatch {
    readonly fetchUsers: () => void;
}

const mapStateToProps = (state: State): ConnectedState => ({
    list: state.users.list,
});

const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => {
    return bindActionCreators({
        fetchUsers: UsersActions.fetchUsers,
    }, dispatch);
};

class Container extends React.Component<ConnectedState & ConnectedDispatch & OwnProps, void> {

    componentWillMount(): void {
        const {fetchUsers} = this.props;
        fetchUsers();
    }

    handleOnClickRow = (user: User) => {
        console.log('Kliknul jsem na radek: ', user);
    };

    render() {
        const {list, fetchUsers} = this.props;
        return (
            <div>
                <Button onClick={fetchUsers}>Refresh</Button>
                <UsersList data={list} onClickRow={this.handleOnClickRow}/>
            </div>
        );
    }
}

export const UsersContainer = connect<ConnectedState, ConnectedDispatch, OwnProps>(mapStateToProps, mapDispatchToProps)(Container);

Uf, vypadá to šíleně, že? Ale pojďme si to rozebrat a vysvětlit, že to má svůj důvod :)

Co se týče importů, tak tam se nic zajímavého neděje, importujeme to, co v daném kontejneru budeme potřebovat.

Poté vytvoříme tři typy pro props.

První je OwnProps, který říká, že pokud bychom komponentu někdě importovali, je možné přes tento interface doplnit vlastní props. Například v případě, že je kontejner stránkou, na kterou se odkazujete přes React Router 4, bude interface vypadat následovně:

Typescript - Own props with React Router 4
import {RouteComponentProps} from 'react-router';

interface OwnProps extends RouteComponentProps<void> {
}

ConnectedState je typ, který definuje, co jsme z Reduxu vlastně získali. Tento interface je přímo spojen s funkcí mapStateToProps.

ConnectedDispatch je typ, který definuje Redux dispatch akce, které budeme v komponentě volat. Tento typ je přímo spojen s mapDispatchToProps.

V mapDispatchToProps si můžete všimnout funkce bindActionCreators, která přijímá dva parametry. Prvním je objekt obsahující dispatch funkce a v druhém je instance dispatch, která je nutná pro vykonání akce. Návratovou hodnotou je objekt obsahující jednotlivé akce, které jsou volány přes dispatch.

Samotná komponenta není až tak zajímavá. Zjednodušeně říká, že ve chvíli, kdy bude poprvé použita, automaticky načte data. Tyto data získáme z Reduxu do props a předáme je komponentě UsersList, která reprezentuje tabulku uživatelů.

Na konci si můžete všimnout, jakým způsobem se provede mapování na Redux. Metoda connect přijímá dva callbacky, což jsou mapStateToProps a mapDispatchToProps a vrací funkci, jejímž parametrem je naše komponenta, kterou chceme napojit.

V tomto případě tedy neexportujeme třídu, reprezentující naší komponentu, ale výsledek metody connect, který obsahuje naší komponentu, obohacenou o napojení na Redux. Zde také můžete vidět, proč existují tři typy pro props. Metoda connect přijímá tři generické typy, které jsme si definovaly na začátku.

Závěr


O Reactu s Typescriptem by se dalo napsat mnoho věcí. Nicméně, velikost článku by poté byl spíše kompletní příručka, což není cílem. Proto nezbývá, než toto téma rozdělit na víc částí. Příště zkusím vysvětlit, proč třeba nepoužívat funkce.bind(this) a jak se tomu vyhnout.

pondělí 12. června 2017

Co mi vadí na React Relay?

Facebook, jakožto technologická firma, je v současné době na vrcholu. Nejenom proto, že dal světu takové věci jako je React či GraphQL, ale také pro to, že jsou to knihovny, které sám Facebook používá. Díky tomu dostanete věc, která je odladěná pro danou činnost a nemusíte se bát, že jí Facebook přestane podporovat. To je asi jeden z největších rozdílů, kterým se vyznačuje Facebook oproti Googlu. Google se v minulosti dost často ukázal jako vrah svých nenarozených dětí. Každý, kdo se kdy setkal s technologiemi jako je Dart, Angular 1 či třeba GWT by mi jistě dal za pravdu.

Nicméně, bylo by příliš krátozraké, kdyby se člověk automaticky adaptoval na to, co Facebook vypustí. Jednou z věcí, přes kterou jsem se nebyl schopen dostat, je Relay.

Dnešní článek bude opačný. Místo, abych se snažil evangelizovat danou technologii, zkusím vysvětlit, proč není Relay zrovna ideální knihovna.

Začněme hezky od začátku.

Co je Relay?


V případě, že začnete používat React a současně GraphQL, dostanete se do fáze, kdy zjistíte, že přesně pro tento "stack" je určen Relay. Zjednodušeně se dá říct, že Relay je abstraktní vrstva nad vašimi React komponentami, která zajišťuje komunikaci s GraphQL serverem a vaší view vrstvou. Odstíní vás od takových věcí jako je samotný "fetch" na server API či nastavování storage, do kterého se budou načtená data ukládat. Dále za Vás bude řešit error handling a vaše komponenty se stanou prezentační vrstvou pro grafové objekty z API. Umí zařídit optimistické mutace, kdy sami říkáte, že server změnu udělá a vy klienta obelstíte tím, že se budete tvářit, jako kdyby k tomu již došlo.

Relay za vás zařídí většinu nudných věcí. To je skvělé. Až na to, že....

Abych byl schopen popsat, co přesně mi vadí, zkusím Relay rozdělit na dvě části, kterých se to týká. Těmi částmi je GraphQL server a React klient.

GraphQL Server


V případě, že se adaptujete na Relay, dostanete studenou sprchu hned na začátku. Zjistíte totiž, že si nemůžete své GraphQL API psát tak, jak se Vám zlíbí. Relay vám bude hodně věcí předepisovat. První z věcí, kterou musíte doplnit je to, že celé query musí být obaleno jedním hlavním query, který v podstatě říká, že se dotazuji na data přihlášeného uživatele. V tom by až takový problém nebyl. Obalíme query jedním root query a je hotovo. Bohužel tomu tak není.

Další věcí, kterou musíte splnit je identifikátor jednotlivých GraphQL objektů. Jinými slovy to znamená, že identifikátor, nebo chcete-li ID musí být unikátní v globálním stavu.

Co to vlastně znamená? 


Představte si situaci, že máte objekt uživatele, který je reprezentován z databáze. V databázi má vlastní ID. Abychom zařídili globální unikátnost, používá se k tomu následující způsob: "K ID uživatele přičtu název GraphQL objektu a vytvořím unikátní hash." V případě zpětného volání dojde k tomu, že GraphQL nejdříve "rozparsuje" daný identifikátor, abychom z něj dostali ID uživatele. Toto celé se děje z prostého důvodu. Relay má na klientovi vlastní storage, ve kterém jsou jednotlivé záznamy uloženy právě s tímto globálním identifikátorem. V případě, že někde dojde ke změne, tak jí propíše všude, kde je daný identifikátor roven.

Opět uvedu příklad.
Mám tabulku online uživatelů, kde je jméno a příjmení. V jiném panelu edituji uživatele, u kterého změním jméno. V případě, že se jedná o záznam, který je použit i v oné tabulce, provede přepis i tam. Tím Relay částečně zajišťuje aktuálnost dat.

Další věcí, kterou musíte splnit jsou kolekce dat. V případě, že používáte pole záznamů (což jistě používáte), musíte tuto kolekci implementovat tak, jak vám předepisuje Relay. Jedná se zejména o stránkování mezi daty.

Poslední věcí, kterou musíte na serveru splnit jsou mutace. Respektive argumenty mutace. Nelze si je napsat jen tak, jak se mi zlíbí. Musí být obaleny přes vlastní GraphQL objekt "input".

Dobrá zpráva je, že pro všechny potřebné změny, pokytuje Relay vlastní knihovny, které v GraphQL objektech buď implementujete, nebo své objekty touto knihovnou obalujete.

Výsledkem je, že vaše GraphQL API bude připravené pro Relay, ale za jakou cenu? Nejen to, že musíte splnit dané podmínky, pro správné fungovaní Relay, ale také nemáte moc možnost z tohoto "uhnout" vlastním směrem.

Relay v tomto případě prohrává 1:0. Je příliš invazivní a příliš me svazuje.

GraphQL Client alias Relay


Nyní se přesuneme směrem ke klientovi.

Po tom, co s potem ve tváři, splníte všechna předepsaná kritéria na straně serveru, tak musíte počítat s tím, že klient na tom není o moc lépe.
Za prvé je nutné celou vaší React aplikaci obalit Relay komponentou, která je obsahuje vlastní store a zařizuje to, aby vám správně fungovalo "injectování" GraphQL dotazů do komponent.
Výsledek je poté následující. Napíšete vlastní komponentu a obalíte jí pomocí Relay, kam zapíšete GraphQL dotaz. Relay poté zařídí, že vaše komponenta dostane výsledná data pomocí "props".

Díky tomu Relay v podstatě nahrazuje nejznámější implemenaci Fluxu a tím je Redux. Pokud byste se totiž rozhodli, že budete chtít používat Relay a k tomu Redux, musím vás zklamat. Tyto dvě technologie jdou částečně proti sobě. Máte Relay a ten vám přeci stačí.

V tom vidím největší nevýhodu na straně klienta. Nejen, že mě nutí používat Relay a zahodit Redux, ale je navíc opět dost invazivní a dost mě uzamyká někde, kde nechci být. Chci mít volnost v tom, jakým způsobem si budu vlastní komponenty navrhovat. Chci si je rozdělit na kontejnery a komponenty. V případě Relay toto neexistuje. Komponenta je totiž často zároveň kontejnerem.

Pro mě Relay na klientu prohrává opět 1:0.

Co s tím?


Pokud se rozhodnete, stejně jako já, že Relay není tou technologií, kterou byste chtěli použít, nabízí se co s tím. Vedle knihovny Relay existuje ještě jedna knihovna a tou je Apollo.

Apollo má výhodu zejména v tom, že je méně invazivní. Nejenom, že vám nabízí, abyste vedle Apolla používali i Redux, ale je i inkrementálně adaptivní. Tedy máte možnost Apollo začít používat i na existujícím projektu, aniž byste museli provést větší refactoring.

Druhou variantou je vlastní řešení.

Vlastní řešení spočívá v jednoduché myšlence. Mám Redux a akce, které ručně vykonávájí načtení dat ze serveru, stejně jako v případě třeba REST API. Prostě si dotazy píšete mimo komponenty a řídíte si sami, co do Reduxu a jak uložíte.

Touto cestou jsem se vydal i já.

Nejenom, že jsem dostatečně flexibilní, ale také jde o to, že samotné dotazy na server jsou velice jednoduché.

Výsledný kód vypadá třeba následovně:

const fetchEdit = (id: string) => {
    return (dispatch: Dispatch<Store>, getState: () => Store) => {
        dispatch({type: LabelActions.FETCHING_LABEL_EDIT});
        const query = `
            query ($id: ID!) {
                label(id: $id) {
                    id
                    name
                    typeId: type {
                        value:id
                        label:key
                    }
                }
            }`;
        client(dispatch, getState)(query, {id}).then((result) => {
            dispatch({type: LabelActions.FETCHED_LABEL_EDIT, payload: result.data.label});
        }).catch((err) => {
            dispatch({type: LabelActions.ERROR_FETCHED_LABEL_EDIT});
            dispatch(MessageActions.setErrorServer(Lang.CONNECTION_ERROR_LABELS_EDIT, err));
        });
    };
};

Metoda načítá data pro editaci:
  1. Nejprve si do reduxu uložím informaci o tom, že dochází k načítání
  2. Poté, co se načtou data, uložím je do reduxu a oznámím, že jsou načtena
  3. V připadě chyby si sám opět zařídím, abych to oznámil do reduxu

Závěr


I přes to, že se dají nalézt výhody, proč se na Relay adaptovat, tak existuje i spoustu důvodů, proč ne. Jedním z hlavních důvodu je to, že pokud se pokusíte použít Relay, bude technologie víc cílem, než cestou. Doufejme, že nová verze, na které se pracuje, bude méně invazivní a umožní i inkrementální adaptaci. Zatím si nechávám Relay v šuplíku a zůstávám u Reduxu s vlastním řešením, které mi vyhovuje víc.

pondělí 5. června 2017

Jak jsem technologicky postavil startup

Tento příběh pojednává o technologiích, nástrojích a vůbec o všem, co jsem potřeboval k tomu, abych byl schopen, postavit startup na zelené louce.

Každý správný příběh začíná stejně: "Jednou jsem...."

Kapitola první: Nápad


Jednou jsem se setkal s člověkem, který měl nápad na produkt, který se v průmyslu zatím nevyskytuje. I přes prvotní skepsi, kdy jsem si říkal: "Tohle už přeci dávno v průmyslu existuje, ne?", jsem došel ke zjištění, že nikoli.

Tím jsem se dostal ke svému prvnímu poučení. Průmysl je technologicky dost zabržděný. Osobně se domnívám, že těch důvodů, proč tomu tak je, je několik. Za prvé je to fakt, že většina lidí, kteří se pohybují v tomto odvětví jsou často konzervativní a za správné považují pouze léty osvědčené věci. Druhým důvodem je to, že jakákoli změna znamená riziko. Ať už z pohledu finanční ztráty tak i z pohledu stability výroby. No a třetím a nejzásadnějším důvodem je to, že ač zde máme spousty technologických vymožeností, narážíme na to, že je obrovský nedostatek lidí. Tedy nejsou programátoři a další specialisté, kteří by dané vymoženosti transformovali do reálné praxe.
Budoucnost nám jistě nabídne spousty nových a zajímavých produktů a jediné, co nás bude brzdit je pracovní síla, která by dané produkty oživila.

Ale zpět k nápadu.

Pochopil jsem, že tohle by mohlo být ono. Zajímavý produkt, postavený na jednoduchém principu. I když zde budete očekávat, že Vám blíže popíši onen nápad, musím Vás zklamat. Neudělám to. Důvodem ani není to, že bych to chtěl tajit, ale spíše to, že tento příběh není zrovna dobrou platformou, kde bych chtěl zabíhat do těchto detailů. Jen zmíním to, proč jsem se rozhodl, opustit projekt, na kterém jsem v té době pracoval a šel do neznáma, které sebou přináší velké riziko neúspěchu.

Tím důvodem byla pouze jediná věc: "Jednoduchý nápad". Co to znamená? Pokud máte nápad na startup, tak platí jedno pravidlo. Měli byste být schopni jednou větou, nebo ještě lépe, pár slovy popsat, o co vlastně jde. Pokud to nelze, máte bud špatný nápad, nebo jste stále nebyli schopni svůj nápad uchopit tak, jak byste měli.

A nyní už více zůstanu u technologií a nástrojů.

Onen nápad znamenal následující:

  1. Napsat android aplikaci
  2. Napsat server aplikaci
  3. Napsat webovou aplikaci
  4. Současně s tím myslet na to, že aplikace musí být multitenantní a v cloudu.


Kapitola druhá: Nástroje


Ještě před tím, než jsem se pustil do prvních proof of conceptů, jsem si navrhnul nástroje, které budou pro takový projekt stěžejní.

Prvním nástrojem, který jsem věděl, že bude bezpodmínečně nutný je repozitář kódu.
Ať už projekt bude v Jave, PHP či Perlu, vždy potřebujete někde uchovávat svůj kód. Samozřejmě, že volba byla jasná: Git. Proč volit něco jiného, když Git už znám a vím, že splní všechny moje požadavky.
Po prozkoumání dostupných možností, jsem nakonec zvolil BitBucket. Jednak z důvodu toho, že i zdarma umí privátní repozitáře a jednak i pro to, že v rámci ekosystému se mi hodilo to, že od stejné firmy máme i další nástroje.

Druhou nutnou věcí byl tracking systém.
Ať už pro bugy, tak i pro samotný vývoj. Nechcete přeci tyto věci udržovat v excelu, mailu či si psát vlastní systém. Zde byla volba velice jednoduchá. Asi nikoho nepřekvapí, že volba padla na Jiru.
Jira splňuje opět všechno, co potřebuji. Mohu ji provozovat v cloudu, aniž bych musel hloupě instalovat systém na vlastním železe a současně s tím pokrývá velkou část důležitých aspektů. Jednak je to zmíněný tracking pro bugy, ale také možnost Jiru využít pro plánování, ať už pro SCRUM či vlastní upravenou metodiku.
Třešničkou na závěr je samozřejmě propojení BitBucketu s Jirou a tím k jednotlivým ticketům přiřazovat commity z GITu.

Třetím nástrojem byla znalostní databáze, nebo chcete-li wiki. 
Jelikož jsem od Atlassianu využíval BitBucket a Jiru, tak volba byla opět jasná. Confluence. Proč používat něco jiného? Confluence znám a navíc vím, že mi třeba umožní zobrazení dat i z Jiry. Stejně jako u předešlých nástrojů, je opět možné Confluence využívat jako službu v Cloudu a proto není stále nutné mít vlastní servery.

Čtvrtým nástrojem byl komunikační kanál. 
Jestli někdo očekává něco ve stylu: "Máme přeci mail, tak pořídíme vlastní mail server či gmail pro firmy, musím ho zklamat." Mail je jeden z nejhorších možných komunikačních kanálů a využití tohoto nástroje jsem se rozhodl minimalizovat na jeho nutnou potřebu.
Jako komunikační kanál jsem zvolil Slack. Znáte Slack? Ne? O hodně přicházíte. Nechci zde rozepisovat, co všechno Slack umí, ale vyjmenuji pouze to, k čemu ho používámě my.
Tak za prvé je to samotná komunikace mezi uživateli. Chcete Frantovi napsat o nových poznatcích? OK. Napiš mu přes chat. Chceš, aby to viděl i Pepa? Pozvi ho do konference a založte si vlastní místnost. Chceš někde projednávat pouze technické věci? Založ místnost a pozvi lidi, kterých se to týká. Sdílej ukázky kódu, apod. Slack toho nabízí skutečně hodně.
Dalším důvodem, proč Slack, je jeho možnost integrace s ostatními systémy. Nejprve jsem integroval Slack s Jirou. Skvělé, mám možnost si přes Slack prohlížet Jira tickety. Nic složitého.
Další integrací byl samotný BitBucket. Chci notifikace o tom, co se děje v GITu? Není problém, přihlásím se o notifikace a vídím, že Franta provedl commit do GITu, kterým rozbil celý build.
Slack stále nemusím instalovat a mohu ho využívat jako službu v Cloudu.
Stále nepotřebuji vlastní server. Díky bohu.

Pátým a posledním nástrojem je testing tool.
Nejsme neomylní a mít nástroj na zápis testovacích scénářů a současně s tím, mít možnost vyhodnocování jednotlivých exekucí testů, je tedy nutné.
Testovacích nástrojů existuje nepřeberné množství.
Nejdříve jsem se vydal slepou uličkou a tou byly ruzné pluginy do Jiry. Bohužel jsem se dostával do stavu, že plugin byl buď nefunkční a nebo byl dost neefektivní. Nakonec jsme skončili u TestTrail. Jednoduchý nástroj na tvorbu testovacích scénářů a současně s tím i exekuci. Samozřejmostí je opět integrace na Jiru, kde Bug do Jiry vytvoříte přímo v TestTrail. Integrace je obousměrná. Tedy na samotném Jira ticketu máte možnost vidět exekuci z TestTrailu. Ideální. Co víc si přát.

Tím bychom měli samotné nástroje. Pojdmě dál...

Kapitola třetí: Google Cloud


Jelikož jsem na začátku zmínil, že samotná aplikace je multitenantní a v Cloudu, tak potřebujete sami nějaký Cloud, kde byste provozovali svojí aplikaci. Stavět si vlastní servery je stejné, jako se střílet do vlastní nohy. A to přeci nikdo nechce.
Variant, které připadají v úvahu je několik. Ať už zvolíte řešení od Amazonu AWS, Microsoftu Azure nebo Googlu, vždy uděláte dobře. Nakonec jsem zvolil Google Cloud. Důvod proč, není postaven tolik na rozumu, jako emocích. Jednak je to z důvodu toho, že mám firmu Google raději než třeba zmíněný Microsoft a jednak i pro to, že víc důvěřuji firmě, která má svůj hlavní byznys postaven na internetu a Cloudu.
Až později jsem zjistil, že Google do samotného Cloudu investuje nemalé peníze a rozvoj je téměř raketový. Jakožto roční uživatel, mohu sám potvrdit, že se opravdu snaží postupně nabízet a vypiplávat jednotlivé možnosti samotného Cloudu. To je skvělé.
Jednou z věcí, kterou na Google Cloudu skutečně miluji, je připojení na server pomocí SSH ve webu. Ano, ve webu. Není nic jednodušího, než kliknout na daný virtuální server, dát "SSH Connect" a v té chvíli se Vám otevře okno s konzolí samotného serveru. Už není nutné žádné otravné předávání SSH klíčů či jiného zabezpečení pro přístup k serveru.

Kapitola čtvrtá: Programovací jazyk a architektura


Možná by se hodilo tyto dvě věci od sebe odlišit, ale osobně si myslím, že daný jazyk dost ovlivňuje samotnou architekturu. Proto psát o těchto věcech rozděleně, nedává smysl.

Jakožto javista jsem měl volbu jasnou. Backend bude na Tomcatu se Springem, JPA a nějakou relační databází. Webové API bude REST. Frontend udělám Angularem či Reactem a mobilní aplikaci hezky v Jave přes Android Studio.

Tak jsem se pustil do práce. Napsal jsem backend přes Spring Boot a Spring Data REST. Databázi jsem zvolil MySQL s tím, že bude možné jí změnit v případě "požadavků". Přeci jen mám JPA, ne? :)
Současně s tím jsem napsal Android aplikaci, která dělala to, co jsem potřeboval. Fungovala na principu, na kterém jsme se na začátku domluvili.

Poté přišel na řadu web.

Teď bych mohl napsat o tom, jak jsem zvolil Angular či React a napsal SPA webovou aplikaci. Jenže zde něco končí a něco nového začíná....

Měl jsem zkušenosti s Angularem 2, ve kterém jsem si napsal aplikaci, která mi pomáhala, jako team leaderovi, vyhodnocovat skutečnost. I když byl Angular 2 v alfa verzi, tak mi to nevadilo. Nešlo o kritickou aplikaci.

Jenže shodou okolností jsem se v té době dostal k přenášce dvou mladých kluků, kteří, ač angularisti, začali hrozně shazovat Angular a velebit React. V té době jsem React znal spíše teoreticky. Veděl jsem, že má virtuální DOM a že bla bla bla...
Jenže oni mě tak moc nahlodali, že jsem se rozhodl, že do toho proniknu více a frontend tím Reactem přeci jen napíšu.

V té době u mě došlo k tomu, že jsem se zamiloval. Ne, slečna to nebyla, tu už jsem měl :) Ale došlo k tomu, že jsem jednak našel krásu v Reactu, ale hlavne ve funkcionálním a deklarativním programování. Na to jsem z Javy nebyl příliš zvyklý. Přeci jen, i když již existovala Java 8 (lambda funkce), tak většinou nemáte moc možností se s ní setkat.

A tak jsem začal přemýšlet, zda bych javascript nemohl využít i jinde. A ono to šlo! Najednou jsem zjistil, že existuje něco, co se jmenuje React Native a že vlastně i mobilní aplikaci mohu napsat stejnou technologií. A jelikož jsem od přírody liný člověk, tak jsem si řekl, že se přeci nebudu učit více věcí najednou, když mi stači jedna s drobnou obměnou.

A tak došlo k tomu, že jsem zavřel Android Studio, smazal projekt v Jave a přepsal ho do React Native. A vše fungovalo. I to zatracené Bluetooth LE mi v Reactu šlapalo.

Poté přišel na řadu backend. Web a mobil mám v javascriptu/typescriptu a backend v Jave. A v té době mi z nějakého neznámého důvodu začala Java vadit. Sakra, psal jsem v tom jazyku více jak 9 let a měl jsem ten jazyk skutečně rád. Jednoduchý, staticky typový a navíc na server aplikace ideální volba. Jenže v té době jsem objevil další Facebook technologii. GraphQL....

Psát o tom, co je GraphQL není předmětem tohoto příbehu. Jen v rychlosti řeknu, že Vám umožňuje napsat webové API tak, jak byste v RESTu či SOAPu nikdy nebyli schopni. Deklarativně a s možností si psát vlastní dotazy do API, kterým budete říkat, co vlastně chcete vrátit.

Tak došlo na první fázi. GraphQL v Jave. Po zjištění, že pro GraphQL existuje knihovna i do Javy, jsem zajásal. Ale jen do doby, než jsem pochopil, že to je asi jako kdybyste traktor pomalovali jako závodní auto. Zjednodušeně řečeno, díky samotnému deklarativnímu zápisu GraphQL to bylo dost neefektivní.

A jelikož jsem se rozhodl, že GraphQL se už nevzdám, tak jsem musel, se slzou v oku, celý java backend zahodit a přepsat ho pomocí Node.JS/Express. Výsledkem bylo, že jsem měl asi desetinový kód, který toho ve výsledku uměl víc, než v samotné Jave.

Timto jsem se rozloučil s Java světem a stal se už pouze nezávislým pozorovatelem. Jazyka, na kterém jsem sám vyrostl a který jsem považoval za nejlepší variantu na backend.

Výsledkem bylo, že jsem získal "full stack", který používal stejný jazyk na všech úrovních. Díky zaplacenému NPM serveru jsem si začal tvořit vlastní privátní balíčky, které poté sdílel mezi mobilem, webem a serverem.

Ideální kombinace. Funkcionální programování je zábava a navíc je o tolik efektivnější.

V té době se ze mě stal evangelista javascriptových technologií a současně s tím i fanatik, který vyznává pouze jednu pravdu :)

Kapitola pátá: Deployment


Co by to bylo za systém, kdyby neuměl automaticky nasazovat aplikaci na server?

První věcí, kterou jsem k tomuto účelu začal využívat je Jenkins. Jenkins je nástroj pro continuous integration. Uložíte projekt do GITu a on provede nějakou událost. Ať už je to spuštění buildu, testu či samotný deploy aplikace.

Jenkins má spoustu výhod a spoustu nevýhod.
Jednou z velkých nevýhod, alespoň pro naše učely, je to, že na svou robusnost nepřináší tolik výhod. Tím myslím to, že sice přes něj mohu provádět všechny potřebné úkony, ale musím si ho nainstalovat na vlastní server (samozřejmě v Google Cloudu), ale za cenu toho, že musím nakonfigurovat mraky věcí.

Po zralé úvaze jsem Jenkins server smazal a přešel na BitBucket Pipelines.
Co jsou to vlastně Pipelines? Je to takový jednoduchý Jenkins, který na základě změny v GITu, může vykonat událost. Tou událostí může být opět cokoli. Kromě toho, že díky BitBucketu, máme Pipelines zadarmo, tak je i možné Pipelines integrovat se Slackem, takže opět win/win.
A jelikož se vracíme k původní myšlence, že naše aplikace má být v Cloudu a multitenantní, tak samotný deploy aplikací musíme nějak rozumně řídit. A k tomuto účelu nám stejně nemůže sloužit ani Jenkins, ani Pipelines, ale něco jiného.

Třetí a nejlepší variantou je vlastní aplikace, hlídající deployment, servery či stav v GITu. Nechci přesně rozepisovat, jak to funguje, ale vychází z jednoduché myšlenky, která říká: "Chci mít možnost, na jednoduchý klik tlačítka nasadit verzi X/Y na server A a přitom kontrolovat, že server A je v té či oné verzi a je na to připraven."

Kapitola šestá: Shrnutí


Rozepisovat o všech technologiích a knihovnách, které používáme, by bylo příliš vyčerpávající a asi i nudné. Jednou z věcí je třeba error tracking systém přes Sentry.io, který nám hlídá stav aplikací. Ale o tom někdy příště v samostatném článku.

Výsledkem mého snažení bylo několik věcí, které teď v bodech shrnu:

  1. Unifikované prostředí pro vývoj
  2. Jednoduchá správa
  3. Rychlá adaptace vývojářů, testerů
  4. Využití moderních technologií, které jsou přínosné
  5. Připravit firmu na větší počet zaměstnanců v IT
  6. Schopnost řídit systém jak technologicky tak projektově
  7. Minimalizovat náklady na vývoj
Závěrem jedno doporučení. 

Zda chcete vědět, jak správně navrhovat aplikace či celé technologické firmy, musíte mít hlavně zkušenosti s tím, jak to nedělat. 

React a hrátky s TypeScriptem

V minulosti jsem se již několikrát zmiňoval, že používat JavaScript bez statických typů, je stejné jako jezdit na kole poslepu. Nemusí se...