Alessio Biancalana Grab The Blaster di Alessio Biancalana

Hello, SUSE!

SUSE Logo

Quando ero piccolo mi capitava spesso che mi chiedessero che mestiere volessi fare da grande, banalmente perché è una domanda che si fa ai bambini. Non penso che valga la pena di riportare su queste pagine la tracotanza smodata delle mie ambizioni di cinquenne, ma in questi giorni ho ripensato a un’altra occasione di questo tipo molto più recente. Avevo all’incirca sedici anni, e stavo parlando con mio padre in maniera più concreta di quali fossero le mie inclinazioni: mi ricordo che proprio qualche ora prima avevo finito di aggiornare una pagina wiki di qualche software che stavo usando, e gli dissi “guarda papà, se fosse possibile io vorrei lavorare con queste aziende che fanno tutta ‘sta roba di Linux”.

Fast-forward a quasi quindici anni dopo: vedo che SUSE sta aprendo una carrellata di posizioni in Europa piuttosto interessanti, specialmente una in linea con le mie abilità attuali, e mi candido più per gioco che per altro.

Ho iniziato a fare i colloqui senza una reale aspettativa, sapevo che il clima del team e del portfolio erano buoni, avevo un amico che era stato assunto da poco in una posizione simile1, quindi non posso dire di essermi candidato col più vivo dei sentimenti. Il mio morale era a dire il vero piuttosto basso, per tutta una serie di motivi.

Quello che poi è successo però è che ho avuto una chiamata di primo contatto, poi altre call per i colloqui veri e propri, e ogni passo aggiungeva un po’ di fregola e di emozione perché mi sembrava un po’ impossibile. Ad oggi, invece, posso dirlo fieramente.

Il primo settembre ho iniziato il mio viaggio con SUSE come Senior Software Engineer, Web Development, ed è una grossissima emozione per me che in questi giorni mi sento come un bambino in un negozio di caramelle. Probabilmente è stupido da parte mia, me ne rendo conto, ma avendo utilizzato Linux per metà della mia vita e avendone sempre appoggiato soprattutto gli aspetti filosofici chiaramente non posso negare che tutto questo per me abbia un peso enorme.

Avendo già scritto cinque pallosi paragrafi su quanto sia felice di tutto questo, l’unica cosa che posso dire è che non vedo l’ora di “have a lot of fun” :-)

  1. al quale devo un immenso ringraziamento perché in verità mi ha consentito di passare da lui come referral: grazie Fabrizio

Recensione: Domain Modeling Made Functional, di Scott Wlaschin

Domain Modeling Made Functional

Ed eccoci qui con una nuova recensione: in questo periodo oltre a ripassare le basi ho deciso di rendere leggermente più formale la mia preparazione in termini di functional programming, specialmente nei termini di quella zona grigia che è il punto di contatto tra il mondo accademico, come viene tradotta una dimostrazione in codice (che è davvero dove un linguaggio fortemente espressivo dà il suo meglio) e il mondo del business, in particolare delle applicazioni web, dove questi principi di design possono aiutare a far crescere una codebase nella maniera più pulita possibile - soprattutto guardando al codice attraverso la lente del tempo.

Domain Modeling Made Functional viene in nostro soccorso proprio per assisterci durante alcune scelte all’inizio di un nuovo progetto, dotandoci di un po’ di disciplina, insegnandoci un po’ di Domain-Driven Design, e mostrandoci senza troppi giri di parole come il DDD viene espresso al suo meglio attraverso la programmazione funzionale.

Grossomodo il discorso viene portato avanti attraverso vari punti, primo tra cui il type system di un linguaggio funzionale. Nel libro viene usato F#, personalmente ho trovato che tantissimi esempi si adattassero al modo che il manuale e il compilatore di Rust consigliano di trattare il codice, specialmente nella gestione dei side-effect (usando i Result). Dalla descrizione del type system viene poi srotolata un po’ di roba utile, come il fatto di usare le firme delle funzioni non tanto come rete di sicurezza a compile-time quanto come una buona documentazione mantenuta in tempo reale di come funziona il dominio.

A quel punto, avendo un dominio descritto in maniera type-driven e le cui funzioni utilizzano una raccolta di tipi ben definita, si passa ai side effect: validazione, parsing, gestione degli errori attraverso il imodello two-track1, chiamata di API esterne, persistenza.

Tutto questo senza minimamente menzionare2 monadi, funtori, applicativi, o altra roba arcana che il lettore non avrebbe nemmeno la forza di approfondire; il vero valore di questo volume è tenere un approccio estremamente semplice, abbandonando i concetti accademici fuori dalla porta e concentrandosi esclusivamente sui benefici di un approccio di questo tipo, risultando in questo modo un super-pregevole distillato di buonissime pratiche che scorre estremamente veloce nella lettura e soprattutto nell’apprendimento.

Un grazie speciale va a Emanuele De Cupis, che me l’ha consigliato.

Se lo volete comprare, questa è la pagina del libro sulla Pragmatic Bookshelf: non c’è modo migliore di spendere i vostri soldi, e potete stare sicuri del fatto che sia un consiglio sincero perché non ci rimedio una lira :-)

  1. Non sapete cosa sia il modello two-track? Non sapevo neanche io che si chiamasse così, ma se fate JavaScript di mestiere e usate le promise è quello che succede se avete una serie di .then() concatenati e alla fine di tutto un .catch() che gestisce l’errore avvenuto in qualsiasi punto della catena. 

  2. Effettivamente viene fatto una volta sola. Un buon portale per chi è alle prime armi e vuole approfondire anche quegli aspetti più matematici legati a quel tipo di strutture dati. 

Kubernetes production-ready in piccolo: deployare un mini-cluster K3s

Linux, gerarchia del filesystem

Qualche settimana fa mi ha amabilmente scritto il cloud provider presso cui è ospitato il cluster Kubernetes di produzione di AllaCarta informandomi del fatto che la beta del loro servizio Kubernetes managed (che ho provato ~a scrocco~ in anticipo fornendo dei preziosi feedback) sta giungendo a una fine. Dopo attimi di panico, mi sono appuntato tre strade possibili da vagliare:

  • Rimanere col provider attuale, pagando, continuando a usare Kubernetes. Non ho tuttavia voglia di diventare povero, per quanto i loro costi siano molto più prevedibili di qualsiasi piattaforma cloud “mainstream”. In fin dei conti, davvero impraticabile. Dato che AllaCarta è gratis a oggi, devo tagliare tutti i costi il più possibile. :-D;
  • Migrare a un metodo di deployment più tradizionale riutilizzando i playbook Ansible scritti in precedenza;
  • Deployare un cluster Kubernetes per conto mio e provare a riapplicare tutta la configurazione del mio cluster di prod.

Marcata come non fattibile la prima alternativa, ho passato una settimanella a cercare di far fare briscola ai miei playbook Ansible con le nuove macchine di staging che avevo deployato, ma ce n’era sempre una tra versioni che non andavano più bene e altro. L’altra sera, così, in preda a un raptus, ho deciso di provare davvero a vedere se deployare un cluster K3s mononodo di test fosse una cosa proibitiva a livello di sforzo e know-how richiesto.

Ho scoperto che invece basta loggarsi su una macchina appena deployata e dare questo comando:

$ curl -sfL https://get.k3s.io | sh -

In questo modo, il nostro terminale sputerà un po’ di informazioni e in pochissimo tempo avremo davvero un master node Kubernetes perfettamente funzionante a cui connetterci. K3s non è esattamente una distribuzione che include tutto quello che le distribuzioni canoniche di Kubernetes si portano dietro, ma valutando i miei bisogni ho deciso che potevo fare a meno di quelle che per me sono caratteristiche marginali.

Per connetterci al cluster da fuori abbiamo bisogno di un kubeconfig, che possiamo trovare in questo modo. Con questo comando otterremo qualcosa del genere:

$ cat /etc/rancher/k3s/k3s.yaml
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyTVRrNU1EWXdNRFF3SGhjTk1qRXdOVEF4TWpFMU16STBXaGNOTXpFd05ESTVNakUxTXpJMApXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyTVRrNU1EWXdNRFF3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSRzNUY0FCVE9hTFRaT1piUG51QlFEbGNtMTAxMEw5VDF3SVR0TUR5TWIKR0s0YitHTG8xenVOQXRETE1uNjhIWlI5UTRYbFJySzJuVWJnVG1ucUhlb2JvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXZ6QlU3bUtTRUF3NFQ5RDJuOWNNCm9FQkRzVlF3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0lVjZzLzN6RmhkTUsxNFpENU5tRGc5ZG9zdUViOXYKaVFsdU93UHp5bmNDQWlFQWxWK2FYWCtnTENZR3dzTXJBNU1yaWkrSGl3T1hlYVJMWUIvWFAvQVR2Mm89Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    server: https://127.0.0.1:6443
  name: default
contexts:
- context:
    cluster: default
    user: default
  name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrRENDQVRlZ0F3SUJBZ0lJZm9FNytndXpLS0V3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOakU1T1RBMk1EQTBNQjRYRFRJeE1EVXdNVEl4TlRNeU5Gb1hEVEl5TURVdwpNVEl4TlRNeU5Gb3dNREVYTUJVR0ExVUVDaE1PYzNmRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJFMnI3ZXp0dmFnZkRhdnMKd2VhL01WclhiV3RXb0ZGc2FRaVlVTXZxMDNiZlNzdXFHWEYyTUlpRzZ1UG1uOEZwNnRMcG9vQzVQWGp4Zmo3RgpNRUxKM2FpalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU010RBV2dCVFA4VDJNNHhOVmlHTlBPT0ZKOG12c1ovUjJnakFLQmdncWhrak9QUVFEQWdOSEFEQkUKQWlCd2dubFl0MUczdGtuR1NQOEViNm5UMHU5MFQvRWZJZnZNWkR2aVBtZTY0QUlnT2M0UjM0aGJqRE1xejlzbgpHY2oyTDdIcFNad1Z2Z3lXd0xhdGFzQjR6Y1k9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUyTVRrNU1EWXdNRFF3SGhjTk1qRXdOVEF4TWpFMU16STBXaGNOTXpFd05ESTVNakUxTXpJMApXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUyTVRrNU1EWXdNRFF3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTSnFaOSsyNkViZ1hVUTllRlIrWFc1eGw3OEptcGdYbHZSVzZQU2M2Q0IKUTNmRk95dkFFRkhGQ1FleGwxbS9GUkluVjVWL1pmZFN6R2pvS3ZpODMzZFRvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXovRTlqT01UVlloalR6amhTZkpyCjdHZjBkb0l3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnZjh5SUdFa3JzcFhINWNTL1pmMVlEUlB5ZXJBQ01SdmwKVFNxUXBRSzJSNVlDSVFDeE10S0Y4R2xzYVRZc1AxK0xRUlFyNUZtSXVvcE81RVJjM2VXYmp3YlJGQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUs5Snkraks3SW02Wm5xUm91Z1Jsa3NsaVFjbENzQlk0NWdTZTFOaEZPWXNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVGF2dDdPMjlxQjhOcSt6QjVyOHhXdGRtyVGR0OUt5Nm9aY1hZdwppSWJxNCthZndXbnEwdW1pZ0xrOWVQRitQc1V3UXNuZHFBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=

Come possiamo vedere, alla voce riguardante l’indirizzo del server abbiamo un affascinante 127.0.0.1 che dobbiamo sostituire con l’IP pubblico della macchina che abbiamo deployato, o con l’IP privato se stiamo dietro una bella VPN. Fatto questo, il file è effettivamente un kubeconfig valido che possiamo salvare e utilizzare facendolo digerire al nostro kubectl. Ci sono vari modi per farlo che spero conosciate; non ho intenzione di ammorbarvi in questa sede spiegandovi quale è il mio preferito.

Dopo qualche secondo, con un kubectl get all dovremmo poter vedere le risorse dentro il nostro cluster mononodo appena deployato, che giustamente saranno prossime allo zero. Prima di iniziare a deployarci delle applicazioni sopra potremmo guardare se è il caso di attaccare al nostro master node qualche worker node in modo da consentire al nostro sistema un po’ di respiro se lo vogliamo far andare a regime.

È necessario innanzi tutto scoprire quale è il token per connetterci al nostro master node:

$ sudo cat /var/lib/rancher/k3s/server/node-token
K10361c7ecce9548dc6b5d1fc32e69b42eeb6e8d00d5e097923d83b9b4604277a9d::server:c44ecc6b22fdd6d7d08ea321798150f8

Dopodiché possiamo tranquillamente deployare un’altra istanza su un’altra macchina informandola di questo token e dell’URL del master node in modo che ci si vada a connettere:

$ export k3s_master_url="https://k3s-master:6443"
$ export k3s_token="K10361c7ecce9548dc6b5d1fc32e69b42eeb6e8d00d5e097923d83b9b4604277a9d::server:c44ecc6b22fdd6d7d08ea321798150f8"
$ curl -sfL https://get.k3s.io | K3S_URL=${k3s_master_url} K3S_TOKEN=${k3s_token} sh -

Con un kubectl cluster-info dovremmo vedere che il nostro nodo nuovo di pacca ha fatto il suo lavoro. A questo punto possiamo iniziare a giocarci, scrivendo la configurazione di cui abbiamo bisogno nei nostro file YAML e applicandola con kubectl apply -f. Questo procedimento usa K3s e un hosting a basso costo, ma con un po’ di fine tuning (soprattutto andando a parare su un hosting più affidabile per le macchine) possiamo deployare i nostri nodi in questa maniera anche in produzione1. Personalmente ancora non ho trovato feature mancanti dentro K3s per progetti piccoli che mi facciano dire “no, K3s non va bene anche per applicazioni di media portata”; per quanto riguarda AllaCarta, soprattutto, questo setup e un po’ di hardening hanno decisamente salvato la giornata e mi hanno permesso di non rinunciare a un modo di lavorare molto più reattivo a cui ormai mi ero piuttosto assuefatto.

  1. In realtà in produzione abbiamo bisogno di una serie di accorgimenti in più, e soprattutto dato che K3s non supporta il multi-master non possiamo proprio stare senza pensieri. Tuttavia, penso che per il setup per la validazione di un prototipo una cosa del genere vada più che bene. 

Io adoro Kubernetes

dottorblaster adora Kubernetes

Stavo pensando di buttare giù un post qualsiasi tanto per ricominciare a scrivere dato che mi lamento di non avere mai tempo, e ho pensato che non vi ho mai palesato un paio di fatti: il primo è che AllaCarta da qualche tempo gira completamente su Kubernetes, il secondo è che, per dirlo con poche parole, io adoro Kubernetes in ogni sua distribuzione e forma.

Come mai? Innanzi tutto va detto che io sono mortalmente affascinato, come i miei lettori di lunga data sanno, dalle opinioni coraggiose, e “Unironically Using Kubernetes for my Personal Blog” è una delle mie preferite. Dopodiché, devo dire che due sono le cose che mi hanno fatto avvicinare a questa tecnologia che volevo apprendere da tempo: il fatto che un formidabile individuo mi abbia convinto a mandare un po’ di commit al progetto k3ai, e il fatto di aver capito che Kubernetes stesse veramente “mangiando il mondo”. Così mi sono spinto un po’ più in là, ho cominciato a studiare e per acquisire un po’ di conoscenza pratica in qualche settimana ho portato AllaCarta da un paio di macchine deployate tramite Ansible a un cluster full-fledged Kubernetes.

Devo dire che a parte lo scollinamento iniziale, è stato abbastanza semplice. Nel farlo ho scoperto persino un po’ di cose su cui da fuori è molto facile soprassedere.

Si fa presto a dire Kubernetes

Innanzi tutto Kubernetes non è “Kubernetes”, ma esattamente come Linux vuol dire tutto e niente esistono svariate distribuzioni di Kubernetes che lo rendono un bundle utilizzabile e uno strumento meno grezzo del core che siamo stati abituati a concepire nei primi anni di vita di questo adorabile orchestrator di quartiere.

Tra le distribuzioni che possiamo vedere più spesso in giro possiamo trovare K3s, MicroK8s, minikube, Rancher Kubernetes Engine. Fanno tutte un po’ la stessa cosa, semplicemente esattamente come le distribuzioni di Linux alcune sono più adatte ad alcuni use case di altre: il mio amico Carmine per esempio sta mettendo in piedi una guida abbastanza esaustiva su come deployare il proprio cluster RKE su delle VPS Hetzner, io invece sono un appassionato di K3s perché è facile come uno schiocco di dita metterci su dei begli ambienti di continuous integration per le proprie applicazioni1.

Kubernetes è una piattaforma

Letto il titolo qui sopra? Molto bene: come tale, Kubernetes è piuttosto flessibile rispetto a quello che ci installiamo sopra, e non intendo solo i classici deployment di applicazioni, che sono la cosa più comune: è possibile estendere le funzionalità del proprio cluster Kubernetes attraverso l’uso di particolari “operator” (oltre che tramite le Custom Resource Definition), ovvero delle estensioni software che provvedono a smazzare il grosso del lavoro per noi quando si presentano tematiche un po’ più complesse e soprattutto generalizzate/generalizzabili, come il provisioning di un cluster PostgreSQL a bordo della nostra infrastruttura.

È molto bene di solito sapere che cosa avviene sotto il cofano di un operatore perché non conoscere profondamente il suo funzionamento può portarci a credere che stia avvenendo qualcosa sulla nostra infrastruttura di produzione che è assolutamente lontano da quello che in realtà sta succedendo, tuttavia penso che ci sia ancora tantissimo terreno inesplorato in tema di Kubernetes operator, che siano molto utili banalmente perché semplificano tanto lavoro, ma che siano un’astrazione da usare solo dopo aver compreso le basi.

Alcuni operatori sono una bomba allucinante, quasi talmente tanto che non è possibile concepire un cluster senza questi installati. Alcuni esempi? Datadog per il monitoraggio, Argo CD per la continuous delivery (attraverso GitOps). Questo ovviamente rischia di rendere la curva di apprendimento dell’ecosistema Kubernetes estremamente ripida, e rischia anche di creare una frammentazione allucinante nell’ecosistema dato che a questo punto non esiste un cluster che sia uguale a un altro. Ma la flessibilità e la potenza portano con loro questi lati negativi, quindi sapete che c’è? Non lamentatevi.

Non sprecate tempo né fiato nel farlo.

Se pensate che sia “troppo”, guess what: l’informatica è un lavoro più duro di quanto credevate. Potete mettervi a studiare o andare a fare i crimpatori di cavi dentro qualche data center - sempre ammesso che lo sappiate fare: crimpare un cavo per bene è un’operazione da osservare con cura.

La tematica dei costi

Infine, il momento della dolorosa come dicono in alcuni ristoranti: ma quanto costa? Heh, il problema è che è vero: Kubernetes non costa come una Lamborghini, ma di sicuro costa come una qualsiasi bella automobile che potete vedere in un concessionario. Il numero delle macchine di cui fare il provisioning lievita o comunque aumenta anche solo un po’ a seconda della strategia che utilizziamo per gestire l’hardware che sta sotto il nostro cluster. Quello che però spesso non viene contato nel foglio di calcolo del budget infrastrutturale sono le ore uomo spese a connettere le macchine tra loro, a far parlare due VPS che stanno sotto un cluster Percona (un esempio tra tanti). Kubernetes rende queste operazioni leggermente più semplici portando in fondo all’anno un risparmio in ore uomo non indifferente banalmente per via dei principi di configuration-as-code che segue. Possiamo salvare la configurazione del cluster in dei file YML e riapplicarla.

Se non avete mai usato configuration-as-code come paradigma, scoprirete quanto può essere utile quando le cose vanno storte. Rideployare un’applicazione in qualche minuto è un fattore da non sottovalutare, specie se come il sottoscritto avete un pet project che fate girare da soli.

Insomma…

Non è facile riassumere tutto quello che uno può imparare in settimane di deep dive su Kubernetes. Personalmente, non ci ho nemmeno provato: questi sono solo alcuni dei concetti che ho elaborato e che sto studiando. Da un punto di vista di piattaforma, Kubernetes è il nuovo Linux e rappresenta la koiné dialektos del mercato applicativo. Una piattaforma che può abilitarvi a fare le cose con uno schiocco di dita se solo studiate a sufficienza per averne la preparazione.

Non fatevi cogliere alla sprovvista.

Un ringraziamento particolare a Andrea Spagnolo, che mi ha fatto da mentore nell’apprendimento delle basi di Kubernetes, ed è stato anche un mio fidatissimo compagno di profanità quando ho provato a usare cert-manager.

  1. O anche deployare degli applicativi completi. K3s è full-K8s compliant rispetto alle API, e anche se è particolarmente indicato per ambienti piccoli non c’è dubbio che possiamo deployarci anche un bell’MVP in produzione. 

Arriva NetNewsWire 6 per Mac

NetNewsWire 6 for Mac

Qualche giorno fa Brent Simmons ha dato l’annuncio del lieto evento: uno dei progetti open source migliori di cui io abbia mai seguito gli sviluppi ha annunciato la versione 6.0, e quel software è proprio NetNewsWire.

Ma cominciamo dall’inizio: cos’è NetNewsWire? NetNewsWire è un lettore di feed RSS che sincronizza anche con Feedly, Feedbin, e un’altra buona caterva di servizi online per leggere i feed. Quest’ultima versione sincronizza anche i feed letti, nel caso in cui non usassimo nessuno di questi servizi, direttamente via iCloud; personalmente io sono un grosso fan dei feed RSS per praticamente qualsiasi cosa, e uso NetNewsWire principalmente come “interfaccia” per quanto riguarda il mio account Feedly.

Ho un’applicazione di questo tipo per ogni dispositivo che uso, il mio Macbook non fa eccezione, e devo dire di non essere mai stato così soddisfatto di un feed reader, anche perché di NetNewsWire posso vedere tutto il codice su Github, che è una cosa molto particolare se consideriamo che stiamo parlando di un’applicazione per Mac.

Brent Simmons ne parla parecchio, e si concentra spesso sullo stimolare questo tipo di sforzo per quanto riguarda l’ecosistema Apple, persino nella guida ai contributi per il suo lettore di feed:

First thing: don’t send money. This app is written for love, not money. :)

NetNewsWire is all about three things:

  • The open web
  • High-quality open source Mac and iOS apps
  • The community that loves both of the above

Supporting all these things takes work.

In poche parole: NetNewsWire è il mio lettore di feed preferito per Mac. Questa nuova versione introduce un bel po’ di novità. È open source. Spero di avervi fatto venire voglia di usarlo.

Member of

Previous Random Next