Faits : A. Dès le 1er septembre 2003, Z._ a travaillé au service de Coopérative X._, à Neuchâtel, en qualité de responsable administrative et de gestion. Elle était membre de la coopérative et elle en a intégré le conseil d'administration dès 2007. Le 1er avril 2010, la coopérative l'a licenciée avec effet au 31 octobre suivant, et elle l'a libérée de l'obligation de travailler dans l'intervalle. Z._ s'est opposée à ce congé qu'elle tenait pour abusif. Elle percevait alors un salaire annuel brut au montant de 90'285 fr. pour un taux d'activité de 80%. Le 25 août 2011, Z._ a ouvert action contre la coopérative devant le Tribunal civil de l'arrondissement du Littoral neuchâtelois et du Val-de-Travers. La défenderesse devait être condamnée à payer 44'039 fr.50 à titre d'indemnité pour licenciement abusif et 6'000 fr. pour rémunération d'heures de travail supplémentaires, le tout avec intérêts au taux de 5% par an dès le 1er novembre 2010. La défenderesse a conclu au rejet de l'action. Le tribunal a interrogé les parties ou leurs représentants, et il a recueilli divers témoignages. Il s'est ensuite prononcé le 26 août 2016. Il a retenu que le congé était abusif et il a alloué à la demanderesse une indemnité chiffrée à 30'095 fr., montant net, correspondant à quatre mois de salaire, avec intérêts au taux de 5% par an dès le 1er novembre 2010. Pour le surplus, le tribunal a rejeté l'action. La Cour d'appel civile du Tribunal cantonal a statué le 15 mars 2017 sur l'appel de la défenderesse. Elle a rejeté cet appel et confirmé le jugement. B. Agissant par la voie du recours en matière civile, la défenderesse requiert le Tribunal fédéral de rejeter entièrement l'action. Des conclusions subsidiaires tendent à l'annulation de l'arrêt de la Cour d'appel et au renvoi de la cause à cette autorité pour nouvelle décision. La demanderesse conclut au rejet du recours. Les parties ont spontanément déposé une réplique et une duplique.