Topluluk ve tasarım
Aşağıdaki yazıyı 2007 Kasım'ında yazmaya başlamış ancak daha sonra ilerde devam ederim diyerek taslak halinde bırakmışım, arşivde gezerken fark ettim ve yarımda olsa yayınlamaya karar verdim. Kime, ne yardımı dokunur bilemiyorum. Katkıda bulunabilir, devamını getirebilirsiniz.
Grafik ve tasarım birbirinden ayrılmaz ikili olsa da her ikise birbirinden farklı işlerdir. Grafik oranı düşük ancak temiz öğeler ile hazırlanmış pek çok site dolaşıyor etrafta ve bunların pek çoğu web 2.0 kavramı ile hareket ediyor. Hayır öyle janjanlı değil sadece temiz yalın tasarımları olan ve içeriği kullanıcıların belirlediği siteler. Peki 10 sene önceki web'te olup bitenler ve bugün'ü karşılaştırdığınızda neler görüyoruz? Yani akımları ve modaları kastediyorum. 10 sene önce arkaplanda yanıp sönen yıldızlar vardı. her taraf sadece hareketli olsun diye donatılmış gifler vardı. Ve insanlar internette bir şeyler yapmaya çalışıyordu (I kiss you). Kimileri başardı kimileri başaramadı. Başaranlar yeni akımlar oluşturmaya veya onları takip etmeye devam ettiler. 10 sene öncesinin arka planında ışıklar, yıldızlar olan siteleri hala ayakta ve bir taraftada yeni anlayışın oluşturduğu siteler var. Peki kimler hangilerini tercih ediyor ve neden?
Kişisel olarak izlenimim dünün genç olanları, geliştirdikleri unsurları/fikirleri kullanan siteleri tercih ettiği yönünde. Yani eğer 25 yaş üstü iseniz ve şimdiye kadar sadece chat yapmak yerine bir takım oluşumları takip etmiş bir şeyler yapmak için çaba göstermiş iseniz içeriği kendinizin belirleyebildiği siteleri tercih ediyorsunuz.
Oysa dünün orta yaşlıları veya interneti hatun/erkek düşürme mekanı için sınırsız bir kaynak olarak görenler hala arka planında yıldızlar parlayan siteleri tercih ediyorlar çünkü ne kendilerini ne de içinde bulundukları toplulukları geliştirmek yönünde hiç bir çaba gösterme gereği duymadılar. Zevkleri ve görüşleri aynı kaldı. İnce ve temiz tasarıma sahip toplulukların içerisinden zeki ve kültürlü insanların bulunması, hala bir şeyler yapma isteğini dürtükleyen ve bunun için çaba gösteren insanların bulunmasından ibaret.
Web site istatistikleri ve collectd
Bir web sitesinin büyüme istatistiklerini oluştururken ilk başlarda bir hesap tablosu yeterli olabilmektedir ancak büyüme devam ettikçe bu hesap tablosu anlamsızlaşacak ve büyümenin yavaşladığı veya düştüğü zamanlar için geriye dönük izleme yapmak zorlaşacaktır, bu durumda patronlara elimizdeki veriyi daha anlaşılır cicili bicili grafikler çıkartacak ufak tefek yazılımlar hazırlamak bizim en önemli vazifemiz haline gelir ve buda takdir edersiniz ki sıkıcı bir iştir.
Yıllardır pek çok farklı ürün/servisde kullanılan rrd ve rrd araçları bu sıkıcı işleri bizim yerimize yapabilir ancak bu seferde rrd komutuna ait parametreleri neyi nasıl yaptığını öğrenmemiz, deneyimlememiz gerekiyor. Elbette rrd ve araçlarını kullanan farklı uygulamalar için eklentiler yazabilir ve ağ trafiği, bağlantı durumları, yük vb. sistem izleme araçlarının yanında kendi yazılımımız için grafikler oluşturulmasını sağlayabiliriz. Aşağıda collectd isimli veri toplama aracının dbi modülünü kullanarak bir web sitesinin kullanıcıl sayılarını alan ufak bir yapılandırma mevcut. Aşağıdaki yapılandırma sayesinde günlük kullanıcı sayısındaki ivmeyi görebiliyoruz. Birde buna her gün kayıt olan kişilerin sayılarını eklersem eğer tam olacak
<Plugin dbi> <Query "users"> Statement "SELECT count(*) as count FROM users" <Result> Type "gauge" InstancePrefix "users" #InstancesFrom "count" ValuesFrom "count" </Result> </Query> <Database "xxxxx"> Driver "mysql" DriverOption "host" "xxxxxxx" DriverOption "username" "xxxxxxxxx" DriverOption "password" "xxxxxxxxx" DriverOption "dbname" "xxxxxxxxx" SelectDB "xxxxxxxxxx" Query "users" </Database> </Plugin>
bu yapılandırmanın ürettiği grafik ise aşağıda

(Yazar) Sevgili günlük
(Günlük) .....
(Yazar) Yeniden biçimlerdir beni.
(Günlük) Mucizeler departmanı yukarda canım.
Lucene + Zemberek + Cassandra = Lucandra
Lucandra, lucene indexlerini Cassandra'ya yazarak indexlerin birden fazla makinede tutulmasını (partition ve replication) sağlıyor, böylece ölçeklenebilir lucene indexleri elde edilerek arama için tek noktada oluşacak bir hata ile arama özelliğinin devre dışı kalmasının önüne geçiliyor.
Bir süredir bu proje benimde ilgimi çekiyordu ancak Türkçe için yeterli değildi, zira Türkçe için kök bulmak problemli idi ve buna bir çare bulmak gerekiyordu, 2 sene önce açık kaynak günlerinde bu konu ile ilgili olarak yapılan çalışmalar arasında kök bulucu olarak zembereğin kullanıldığını duymuş ve kök bulmak için neden lucandra'ya zemberek eklemiyorum diye kendi kendime sormaya başlamıştım. İlk bir kaç günlük tırsıntı ve kaynak kod okuma ile geçen zaman sonrasında projenin forklanması ve toplam 3 ana commit ile (daha fazla commit'im var ama içlerinden 3'ü önemli) zemberek kök bulucu sistemi projeye eklenmiş oldu. Sadece lucene ile değil solr ile de kullanılabilinir durumda. Zembereğin eklenmesi için yaptığım değişiklikler, 1 satır silmek, 5 satır eklemek, 1 satırda değişiklik yapmak, 3 adet kütüphaneyi (zemberek-cekirdek, trlucene, zemberek-tr) lib dizinine eklemekden ibaret, tabii birde kök bulucu içerisinde yer alan "gereksiz kelime"lerin güncellenmesi var.
Şimdilik sadece Türkiye Türkçesi ile çalışıyor ancak Azeri Türkçesi ve Türkmence Türkçesi için de çalışmasını isterseniz tek yapmanız gereken bu iki dile ait zemberek kütüphanelerini lib dizinine eklemek ve başlangıç sırasında -Dlucene.analyzer= parametresi ile hangi dil için analiz yapılacağını belirtmenizden ibaret.
Henüz Türkçe desteği mainstream'de yer almıyor (pull request gönderdim, bekliyorum), eğer denemek isterseniz http://github.com/selam/Lucandra adresinden kaynak kodları alabilirsiniz.
Düşünüyorum
V8'in gayet güzel bir arabirimi var, nodejs projesinden de görülebileneceği gibi geliştirme yapmakda oldukça kolay, javascript oldukça yaygın bir dil ve geniş bir kullanıcı ağı var.
Peki, acaba linux kerneli ve V8 javascript engine kullanan bir işletim sistemi olamaz mı? Linux boot sürecinde init yerine V8'i çalıştırsa ve V8 her bir terminal için javascript shell çalıştırsa? şu anda V8'in shell erişimi sadece bir kaç ufak tefek komutdan oluşuyor ancak güzel bir geliştirici katkısı ile hazır haline geçmesi pek sorun olmaz diye düşünüyorum. sistem üzerinde bulunacak tüm kütüphaneler sadece ve sadece kernel'in ve V8'in ihtiyaç duyacağı kütüphaneler olsa, bir JsOS'un prematüre hali olabilir mi?
(Yazar) - Sevgili günlük
(Günlük) - Her istediğinde hatırlayacağın birimiyim ben?
(Yazar) - ...........
(Günlük) - Aleyhinde kullanacağım
Cassandra
Cassandra, facebook tarafından geliştirilmiş ve daha önce amazon tarafından geliştirilen Dynamo ile benzer yapıya sahip yapısal key/value veri tabanıdır. Cassandra veri modeli oldukça basittir, geliştirme yapmak oldukça hızlı ve eğlencelidir. Ancak eğlenmeden önce Cassandra'nın veri modellerini anlamak gerekiyor.
Cassandra veri modeli için Keyspace, ColumnFamily, Column, Key, SuperColumn tanımlarını kullanır.
Keyspace
Keyspace basitçe verinizin ait olduğu veri tabanı adıdır diyebiliriz. Keyspace'ler kurulumunuzun yapılandırmasına bağlı olarak /var/lib/cassandra/data/ dizini içerisinde bulunurlar. Eğer SQL'e aşına iseniz SQL'de bulunan veri tabanı adı ile Cassandra'da bulunan Keyspace'in aynı anlama geldiğini düşünebilirsiniz. SQL'den farklı olarak istemciden Cassandra'ya açılan bağlantının hangi veri abanına ait olduğunu söyleyemezsiniz, başka bir değiş ile "use dbname" gibi bir deyim Cassandra üzerinde bulunamamaktadır bu nedenle verilerinizi yazarken veya okurken verinizin hangi "Keyspace" üzerinde olması gerektiğini Cassandra'ya söylemelisiniz.
ColumnFamily
ColumnFamily ise, Keyspace içerisinde bulunan gruplanmış verileri taşıyan tablo olarak düşünülebilirsiniz. Bu tablolar verinize ait satırları tutmaktadır. SQL'den aşina olduğumuz sütünların row haline gelmiş halleri olarak düşünebilirsiniz ancak SQL'de tablo tanımı sırasında belirtilen sütünların ColumnFamily içerisinde tanımlanmasına gerek yoktur. Bir Keyspace içerisinde istediğiniz kadar ColumnFamily tanımlayabilirsiniz.
Key
Key'ler ColumnFamily içerisinde bulunan ve SQL'deki primaryKey alanına denk gelen, verilerinize erişmek için kullanabileceğiniz alanlardır. Key içerisinde bulunan veriler Cassandra için birer "Satır"dır. Ancak bu satırların SQL'de bulunan satırlar olmadığını hatırlamalısınız. Bir ColumnFamily içerisinde istediğiniz kadar Key barındırabilirsiniz.
Column
Column ise Key'lerin içerisinde bulunan sütunlardır. Bu sütunların yapısı SQL'de bulunan sütünlardan oldukça farklıdır. sütunlar içerisinde 3 adet alan bulunmaktadır. Bu alanlar "name", "value" ve "timestamp"'dir. Tabloların SQL'deki gibi bir yapısı olmadığını söylemiştik, işte bu nedenle Column içerisinde name ve value değerleri bulunur timestamp ise karışıklığı önlemek içindir. Tüm yapının anlaşılması için her hangi bir dilde bulunan Array veya hash dictionary'den faydalanabiliriz. Bir key içerisinde istediğiniz kadar Column tanımlayabilirsiniz.
$Keyspace = Array( 'ColumnFamily' => Array( 'Key' => Array( 'ad' => Array('value' => 'Timu', 'timestamp' => '1270663849'), 'soyad' => Array('value' => 'Eren', 'timestamp' => '1270663849') ) ) );
Keyspace = dict( ColumnFamily = dict( Key = dict( ad = dict('value' = 'Timu', 'timestamp' = '1270663849'), soyad = dict('value' = 'Eren', 'timestamp' = '1270663849') ) ) )
Column içerisinde yer alan timestamp, verinin tutarlılığını sağlamak için bulunmaktadır ve istek sırasında istemci tarafından gönderilir. Eğer Cassandra'ya aynı anda verinin güncellemesi için iki istek gönderilir ancak bu isteklerden biri network problemleri veya farklı data center'lardan gönderilmesi nedeni ile cassandra'ya geç ulaşır ise timestamp değeri mevcut verinin üzerinde bulunan timestamp değerinden küçük olacağından güncelleme işlemi yapılmayacaktır. Cassandra üzerindeki verilere "Key"'ler ile ulaşabileceğinizi söylemiştik, isterseniz bir Key içerisinde bulunan Column'lardan tümüne veya bir kısmına erişebilirsiniz.
SuperColumn?
SuperColumn'ları Key'ler içerisinde bulunan bir ColumnFamily olarak düşünebilirsiniz. ColumnFamily cassandra yapılandırma dosyasında (storage-conf.xml) tanımlanır ve SuperColumn olup olmadığı ColumnType öz niteliğinde belirtilir.
ColumnType değeri "Super" verilir ise o ColumnFamily bir SuperColumn demektir. Burada dikkat etmeniz gereken ColumnFamily'i SuperColumn olarak tanımladığınızda Key içerisinde Column türünden veri barındıramazsınız.
$Sinelist = Array( // Keyspace 'Users' => Array( // ColumnFamily 'timu' => Array( // Key 'information' => Array( // SuperColumn, similar as ColumnFamily 'ad' => Array('value' => 'Timu', 'timestamp' => '1270663849'), // Column 'soyad' => Array('value' => 'Eren', 'timestamp' => '1270663849') // Column ), 'images' => Array( // another SuperColumn 'thumbnail' => Array('value' => 'http://static.sinelist.com/users/xxxx.jpg', 'timestamp' => '1270663849') ) ) ) );
Tıpkı ColumnFamily içerisinde ki Key'e ait sütunların tamamına veya bir kısmına nasıl erişiyor isek aynı şekilde SuperColumn için de erişebiliriz. SQL'de bulunan tablolar arası ilişkiler Cassandra üzerine bulunmamaktadır, dolayısı ile veriler arası ilişkileri uygulama tarafında halletmek zorundasınız.
Sıralama
Şu ana kadar Cassandra'nın verileri nasıl organize ettiğini anlamış olduk. Ancak dikkat etmemiz gereken bir unsur daha var ki o da
sıralama. Cassandra'ya verinizi insert ettiğinizde nasıl bir sıralama ile tutması gerektiğini söyleyebilirsiniz, böylece veriyi her zaman sıralı olarak alabilirsiniz.
Cassandra üzerine bulunan Column'lar her zaman Column adına göre sıralı olarak tutulur. Bu sıralama storage-conf.xml dosyasında ColumnFamily tanımlanırken CompareWith öz niteliğine verilen değer ile belirlenir, cassandra ön tanımlı olarak BytesType, UTF8Type, LexicalUUIDType, TimeUUIDType, AsciiType, ve LongType sıralama methodlarını kullanabilir. Bu yöntemlerden her hangi birini seçtiğinizde dikkat etmeniz gereken şey Column adlarının belirtilen sıralama algoritması ile uyumlu olması gerektiğidir. Eğer CompareWith parametresi LongType ise sütun adları 64bit (8 byte) integer uzunluğunda olmak zorundadır. Sıralama her zaman büyükten küçüğe doğrudur.
Eğer ColumnFamily bir SuperColumn ise CompareWith parametresi Key isimlerini etkiler ve sıralama key'ler için yapılır. Sütünların sıralanması ise CompareSubcolumnsWith parametresi ile belirtilir.
Sayfalama
Cassandra ile çalışırken sayfalama yapmak istediğinizde ortaya bir sorun çıkar, bu sorun SQL'den alışık olduğumuz offset değerinin sayısal bir değer olmamasından kaynaklanır, Cassandra için offset değeri bir key olmalıdır, Cassandra'ya şu kadar kayıttan sonra bana 10 tane kayıt ver diyemez ancak şu kayıt ile birlikte bana 10 tane kayıt ver diyebilirsiniz. Burada dikkat etmeniz gereken cassandra size verdiği veriler içerisinden belirtmiş olduğunuz key'e ait verilerin de bulunduğu bu yüzden sayfalama yaparken eğer başlangıç değeri verilmemiş ise 10 tane verilmiş ise 11 tane kayıt çekip başlangıç bilgisi verilen kayıdın kullanıcıya döndürülecek cevap içerisinden çıkartmanız gerekebilir.
Replikasyon
Cassandra içerisinde replikasyon için 3 adet yöntem bulunmaktadır, bunlar RackUnaware, RackAware ve DatacenterShard'dır. Rackaware eğer birden fazla data center ile çalışıyor ve içerisinde kendimize ait bir network kurabilmiş isek faydalıdır aksi halde bir anlamı yoktur. Bir Cassandra makinesine gelen yazma isteği replikasyona tabii tutulacak ise replikasyon methoduna göre bir node seçilir eğer data center içerisinde her hangi bir network kurulumuna gitmemiş isek RackUnaware yöntemi bizim için kullanışlı olacaktır. Bu yöntem Cassandra kurulumlarından bir tanesini rastgele seçerek replikasyonu gerçekleştirecektir. Eğer kendi networkümüz var ve replikaslikasyon yöntemi olarak RackAware kulanırsak, cassandra replikasyon için önce farklı bir data center da bulunan bir node aramaya çalışır bulursa replikasyonu gerçekleştirir, eğer bulamaz ise bu sefer aynı data center içerisinden farklı bir rack bulmaya çalışır ve bulursa replikasyonu gerçekleştirir. Cassandra data center'ları ve Rack'leri bulabilmek için gossip (dedikodu) ile oluşturduğu node listesi içerisindeki ip adreslerine bakarak karar verir. Node listesindeki ip adresinin her birinin 2 nci byte'ı kendi ip adresinin 2 nci byte'ı ile aynı ise ve 3'ü byte'ı kendi ip adresinin 3'ncü byte'ından farklı ise aynı data center içerisinde farklı bir rack, eğer 2 nci byte'ı kendi ip adresinin 2 nci byte'ından farklı ise, node'un farklı bir data center da olduğuna karar verir. RackAware de öncelik her zaman farklı bir data center'a replikasyonun gerçekleştirilmesidir. Eğer replicationFactor 2 ve daha fazla verilirse öncelik farklı datacenter ve farklı rack'dir.
Data center 1 (ip aralığı 192.168.1.1 den 192.168.255.254)
Rack 1: (ip aralığı 192.168.1.40 den 80)
node1: 192.168.1.40
none:2 192.168.1.44
none:3 192.168.1.48
Rack 2: (ip aralığı 192.168.2.40 den 80)
node1: 192.168.2.40
none:2 192.168.2.44
none:3 192.168.2.48
Data center 2 (ip aralığı 192.167.1.1 den 192.167.255.254)
Rack 3: (ip aralığı 192.167.1.40 den 80)
node1: 192.167.1.40
none:2 192.167.1.44
none:3 192.167.1.48
Rack 4: (ip aralığı 192.167.2.40 den 80)
node1: 192.167.2.40
none:2 192.167.2.44
none:3 192.167.2.48
Tekerleği yeniden keşfetmek yada mevcut olanları sormak
Sinelist adına büyük dosyaların efektif biçimde kullanıcılara sunulabilmesi için
bir dosya sistemine ihtiyacım var ve bu dosya sisteminin ölçeklenebilir ve hata tölaranslı olması gerekiyor.
mogileFS vb. benzeri dosya sistemleri genel olarak küçük dosyalarda oldukça başarılılar ancak büyük dosyalarda talepte bulunan kullanıcı sayısı da göz ününe alındığında disk vb. fiziksel sınırlarımızıda düşünürsek aynı başarıyı
gösteremiyorlar.
Bu neden ile kullanıcı düzeyinde (kernel ile iletişime direk olarak ihtiyaç duymadan) bir dosya sistemi düşünmekteyim. Temellerini mogileFS'den alan sistem, mogileFS'den biraz farklı olarak kullanıcının erişmek istediği dosyayı disk üzerinde tek bir dosya olarak değil, 10 Mb'lik parçalar halinde pek çok farklı fiziksel disk üzerine
dağıtarak aynı dosyaya farklı zamanlarda erişmek isteyen kişiler için daha yüksek başarım sağlamayı hedefliyor.
mogileFS'in aksine dosya tek bir parçadan oluşmadığı halde dosyayı belirten işaretçin karşılığında her bir parçanın hangi fiziksel makinelerde olduğu, parça büyüklüğü mutlaka SIRALI bir şekilde dosya hakkında bilgi isteyen izleyiciye döndürülmesinin ardından, izleyici her bir parça için sıra ile ilgili node'lardan ilgili dosyayı HTTP ile istekde bulunacak ve network üzerinden edinilen dosya parçası istemciye gönderilmeye başlanacak her bir parça bittikden sonra bir diğer parça için bir başka node'a gidilerek bu süreç ilgili dosya için tüm parçaları kullanıcıya iletilene kadar devam edecek.
Yukarıda tarif etmeye çalıştığım gibi bir dosya sistemi geliştirmeyi denemeden önce hali hazırda mevcut dosya sistemleri ile büyük dosyalar (1GB ve üzeri) için haşır neşir olmuş, bilgisi ve tecrübesi olanlar yol gösterebilirse sevinirim.
Solr ile rastgele sonuçlar
Eğer arama motoru olarak solr kullanıyor ve mevcut veri index'iniz üzerinde rastgele sıralı sonuç getirmek isterseniz iki yönteminiz var, birincisi schema.xml içerisinde bir random sort field'i tanımlamak diğeri uygulamanız üzerinde bu işlemi çözmek, (her ikisinide yapabilirsiniz elbette) random sort alanı sadece verinizi index'lerken veya index'den çıkartırken oluşturulur ve iki veri güncellemesi arasında aynı sıralamayı alırsınız. Bu sizin için yeterli değilse izleyebileceğiniz bir diğer yol, arama kriterleriniz ile sadece 1 satır sonuç dönecek şekilde gerçekleştirmek ve arama kriterlerlerinize uygun olan sonuç sayısını öğrenip bu sayı içerisinden bir başlangıç noktasını rastgele belirledikten sonra tekrar istediğiniz satır sayısı ile aramayı gerçekleştirmek.
1 = Arama kriterlerine göre arama yap ve kaç sonuç olduğunu öğren (100)
2 = istediğim sonuç sayısını toplam sonuç sayısından çıkart, (100-90)
3 = geriye kalan sonuç sayısından rastgele bir başlangıç noktası seç (start = random.intchoice
4 = aramayı tekrarla
Kolay gelsin.
sineapi
Sinelist projesini bir türlü tamamlayamamış ve henüz yapmak istediklerimin %10'nu bile yapamamış olmama rağmen bir süredir bilmemkaçıncı versiyonunu yazmaya başladım, bu sefer kullandığım teknik ise veri tabanı yapısını schemaless bir yapıya çevirmek, herşeyi json objeleri olarak saklamak ve kullanmak. Bununla birlikte daha ölçeklenebilir bir yapı için proje içerisinde yer alan işlevlerin tamamını web arabiriminden çıkartarak sineapi ismini verdiğim bir api'ya geçirdim. projenin tüm ihtiyaçlarını arka tarafta bu api ile hazırlayarak web arabiriminden kullanıcılara sunmayı planlıyorum. Her ne kadar api açık kaynaklı olsada zamanla tüm ihtiyaçlar için api'ı kullanacağımdan projenin şimdilik belirsiz bir noktasında geliştirmelerin tamamını dışarıya açmayabilirim, ancak mevcut kısmı incelendiğinde schemaless bir projenin nasıl olabileceği görülebilinir. Eğer bir servis geliştirmeyi düşünüyor, bu servisi mobil cihazlar, masaüstü ve diğer web uygulamalarının yazılabilmesi için bir api ile verinizi erişime açmak istiyorsanız mevcut halini şu an itibari ile api yetkilendirme için kullanabilirsiniz.
Not: Her türlü kod gözden geçirmeyi başımın üstünde tutarım.
validpie
ValidPie hem yeni iş yerinde, sinelist'in api kısmında kullanmak üzere geliştirdiğim bir veri doğrulama kütüphanesidir, basitçe symfony'nin validator serisinin python versiyonudur diyebiliriz ancak bir form kütüphanesi taşımamaktadır. amaç sadece input validation yapabilmekdir. elbette hali hazırda python için bir çok araç bulunmakta incelediklerim arasında formEncode vb. kütüphanelerde var ancak hiç birisi bir türlü tatmin etmedi, yazıverdim bende.
from validpie.base import ValidPieBasicSchema from validpie.base import ValidPieString class UserValidatorSchema(ValidPieBasicSchema): def setUp(self, options={}, messages={}): self.setValidPies({ 'username': ValidPieString({'required': True, 'min_length': 7 }, {'required': 'boş bırakamazsınız', 'min_length': 'kullanıcı adınız çok kısa'}) }) validatorSchema = UserValidatorSchema({'username': 'aaa'}) if not validatorSchema.isValid(): for error in validatorSchema.getErrors(): print error.getMessage()
Kullanmak isterseniz kendisi http://code.google.com/p/validpie adresinde ikamet etmektedir.
Özgür yazılım, özgür toplum
Richard stallman'ın yazılarından hazırlanan bu kitabı fazlamesai'de haberini gördüğüm gün siparişini vermiştim ve bu gün elime ulaştı. mutluyum heyecanlıyım. Yazılım'ın sadece bir "iş" ve bu işin etrafındaki yönelimler olmadığını zaman zaman hatırlama ihtiyacı hissediyordum, benim gibi zaman zaman "ne yapıyoruz" diye düşünenler için göz attığım kadarı ile iyi bir kitap olduğu kanaatindeyim.